summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/dictionary.cpp5
-rw-r--r--core/dictionary.h1
-rw-r--r--core/image.cpp112
-rw-r--r--core/image.h2
-rw-r--r--core/math/aabb.cpp4
-rw-r--r--core/math/bsp_tree.cpp10
-rw-r--r--core/translation.cpp8
-rw-r--r--core/vector.h13
-rw-r--r--doc/classes/Image.xml6
-rw-r--r--drivers/dummy/texture_loader_dummy.cpp4
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp5
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h1
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp1
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.cpp2
-rw-r--r--editor/editor_node.cpp1
-rw-r--r--editor/editor_themes.cpp3
-rw-r--r--editor/icons/icon_soft_body.svg56
-rw-r--r--editor/plugins/script_editor_plugin.cpp2
-rw-r--r--editor/plugins/spatial_editor_plugin.h1
-rw-r--r--editor/project_manager.cpp207
-rw-r--r--editor/spatial_editor_gizmos.cpp108
-rw-r--r--editor/spatial_editor_gizmos.h17
-rw-r--r--modules/bullet/bullet_physics_server.cpp174
-rw-r--r--modules/bullet/bullet_physics_server.h43
-rw-r--r--modules/bullet/collision_object_bullet.cpp2
-rw-r--r--modules/bullet/register_types.cpp4
-rw-r--r--modules/bullet/soft_body_bullet.cpp474
-rw-r--r--modules/bullet/soft_body_bullet.h103
-rw-r--r--modules/bullet/space_bullet.cpp8
-rw-r--r--modules/bullet/space_bullet.h6
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp2
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/editor/bindings_generator.cpp36
-rw-r--r--modules/mono/glue/collections_glue.cpp240
-rw-r--r--modules/mono/glue/collections_glue.h100
-rw-r--r--modules/mono/glue/cs_files/Array.cs335
-rw-r--r--modules/mono/glue/cs_files/Dictionary.cs401
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs29
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp32
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp45
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp173
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp19
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp129
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h31
-rw-r--r--modules/websocket/emws_client.cpp1
-rw-r--r--platform/android/audio_driver_opensl.cpp8
-rw-r--r--platform/android/dir_access_jandroid.cpp1
-rw-r--r--platform/android/export/export.cpp2
-rw-r--r--platform/android/java_glue.cpp4
-rw-r--r--platform/android/thread_jandroid.cpp2
-rw-r--r--platform/iphone/export/export.cpp6
-rw-r--r--scene/2d/line_builder.cpp2
-rw-r--r--scene/3d/collision_object.h1
-rw-r--r--scene/3d/collision_shape.h1
-rw-r--r--scene/3d/mesh_instance.cpp1
-rw-r--r--scene/3d/mesh_instance.h1
-rw-r--r--scene/3d/physics_body.cpp20
-rw-r--r--scene/3d/soft_body.cpp740
-rw-r--r--scene/3d/soft_body.h197
-rw-r--r--scene/gui/text_edit.cpp9
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/dynamic_font.cpp4
-rw-r--r--scene/resources/mesh.cpp53
-rw-r--r--scene/resources/mesh.h4
-rw-r--r--scene/resources/surface_tool.cpp1
-rw-r--r--servers/audio/audio_rb_resampler.cpp2
-rw-r--r--servers/physics/collision_solver_sat.cpp3
-rw-r--r--servers/physics/physics_server_sw.h66
-rw-r--r--servers/physics_server.h66
-rw-r--r--servers/visual/rasterizer.h1
-rw-r--r--servers/visual_server.cpp155
-rw-r--r--servers/visual_server.h4
76 files changed, 3830 insertions, 502 deletions
diff --git a/core/dictionary.cpp b/core/dictionary.cpp
index d68411a572..42d9eab310 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -140,6 +140,11 @@ void Dictionary::erase(const Variant &p_key) {
_p->variant_map.erase(p_key);
}
+bool Dictionary::erase_checked(const Variant &p_key) {
+
+ return _p->variant_map.erase(p_key);
+}
+
bool Dictionary::operator==(const Dictionary &p_dictionary) const {
return _p == p_dictionary._p;
diff --git a/core/dictionary.h b/core/dictionary.h
index 84a5cafe1d..00ec67fb99 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -66,6 +66,7 @@ public:
bool has_all(const Array &p_keys) const;
void erase(const Variant &p_key);
+ bool erase_checked(const Variant &p_key);
bool operator==(const Dictionary &p_dictionary) const;
diff --git a/core/image.cpp b/core/image.cpp
index b9386a1b6d..19440d1718 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -33,6 +33,7 @@
#include "core/io/image_loader.h"
#include "core/os/copymem.h"
#include "hash_map.h"
+#include "math_funcs.h"
#include "print_string.h"
#include "thirdparty/misc/hq2x.h"
@@ -525,7 +526,7 @@ static double _bicubic_interp_kernel(double x) {
}
template <int CC>
-static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
+static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
// get source image size
int width = p_src_width;
@@ -555,7 +556,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi
// initial pixel value
- uint8_t *dst = p_dst + (y * p_dst_width + x) * CC;
+ uint8_t *__restrict dst = p_dst + (y * p_dst_width + x) * CC;
double color[CC];
for (int i = 0; i < CC; i++) {
@@ -583,7 +584,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi
ox2 = xmax;
// get pixel of original image
- const uint8_t *p = p_src + (oy2 * p_src_width + ox2) * CC;
+ const uint8_t *__restrict p = p_src + (oy2 * p_src_width + ox2) * CC;
for (int i = 0; i < CC; i++) {
@@ -600,7 +601,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi
}
template <int CC>
-static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
+static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
enum {
FRAC_BITS = 8,
@@ -655,7 +656,7 @@ static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src
}
template <int CC>
-static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
+static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
for (uint32_t i = 0; i < p_dst_height; i++) {
@@ -676,6 +677,16 @@ static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_
}
}
+static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) {
+
+ uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256);
+
+ for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) {
+
+ p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8;
+ }
+}
+
void Image::resize_to_po2(bool p_square) {
if (!_can_modify(format)) {
@@ -707,6 +718,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
ERR_FAIL();
}
+ bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */;
+
ERR_FAIL_COND(p_width <= 0);
ERR_FAIL_COND(p_height <= 0);
ERR_FAIL_COND(p_width > MAX_WIDTH);
@@ -717,6 +730,32 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
Image dst(p_width, p_height, 0, format);
+ // Setup mipmap-aware scaling
+ Image dst2;
+ int mip1;
+ int mip2;
+ float mip1_weight;
+ if (mipmap_aware) {
+ float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f;
+ if (avg_scale >= 1.0f) {
+ mipmap_aware = false;
+ } else {
+ float level = Math::log(1.0f / avg_scale) / Math::log(2.0f);
+ mip1 = CLAMP((int)Math::floor(level), 0, get_mipmap_count());
+ mip2 = CLAMP((int)Math::ceil(level), 0, get_mipmap_count());
+ mip1_weight = 1.0f - (level - mip1);
+ }
+ }
+ bool interpolate_mipmaps = mipmap_aware && mip1 != mip2;
+ if (interpolate_mipmaps) {
+ dst2.create(p_width, p_height, 0, format);
+ }
+ bool had_mipmaps = mipmaps;
+ if (interpolate_mipmaps && !had_mipmaps) {
+ generate_mipmaps();
+ }
+ // --
+
PoolVector<uint8_t>::Read r = data.read();
const unsigned char *r_ptr = r.ptr();
@@ -734,13 +773,57 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break;
}
} break;
- case INTERPOLATE_BILINEAR: {
+ case INTERPOLATE_BILINEAR:
+ case INTERPOLATE_TRILINEAR: {
+
+ for (int i = 0; i < 2; ++i) {
+ int src_width;
+ int src_height;
+ const unsigned char *src_ptr;
+
+ if (!mipmap_aware) {
+ if (i == 0) {
+ // Standard behavior
+ src_width = width;
+ src_height = height;
+ src_ptr = r_ptr;
+ } else {
+ // No need for a second iteration
+ break;
+ }
+ } else {
+ if (i == 0) {
+ // Read from the first mipmap that will be interpolated
+ // (if both levels are the same, we will not interpolate, but at least we'll sample from the right level)
+ int offs;
+ _get_mipmap_offset_and_size(mip1, offs, src_width, src_height);
+ src_ptr = r_ptr + offs;
+ } else if (!interpolate_mipmaps) {
+ // No need generate a second image
+ break;
+ } else {
+ // Switch to read from the second mipmap that will be interpolated
+ int offs;
+ _get_mipmap_offset_and_size(mip2, offs, src_width, src_height);
+ src_ptr = r_ptr + offs;
+ // Switch to write to the second destination image
+ w = dst2.data.write();
+ w_ptr = w.ptr();
+ }
+ }
- switch (get_format_pixel_size(format)) {
- case 1: _scale_bilinear<1>(r_ptr, w_ptr, width, height, p_width, p_height); break;
- case 2: _scale_bilinear<2>(r_ptr, w_ptr, width, height, p_width, p_height); break;
- case 3: _scale_bilinear<3>(r_ptr, w_ptr, width, height, p_width, p_height); break;
- case 4: _scale_bilinear<4>(r_ptr, w_ptr, width, height, p_width, p_height); break;
+ switch (get_format_pixel_size(format)) {
+ case 1: _scale_bilinear<1>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ case 2: _scale_bilinear<2>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ case 3: _scale_bilinear<3>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ case 4: _scale_bilinear<4>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break;
+ }
+ }
+
+ if (interpolate_mipmaps) {
+ // Switch to read again from the first scaled mipmap to overlay it over the second
+ r = dst.data.read();
+ _overlay(r.ptr(), w.ptr(), mip1_weight, p_width, p_height, get_format_pixel_size(format));
}
} break;
@@ -759,7 +842,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
r = PoolVector<uint8_t>::Read();
w = PoolVector<uint8_t>::Write();
- if (mipmaps > 0)
+ if (interpolate_mipmaps) {
+ dst._copy_internals_from(dst2);
+ }
+
+ if (had_mipmaps)
dst.generate_mipmaps();
_copy_internals_from(dst);
@@ -2404,6 +2491,7 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST);
BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR);
BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC);
+ BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR);
BIND_ENUM_CONSTANT(ALPHA_NONE);
BIND_ENUM_CONSTANT(ALPHA_BIT);
diff --git a/core/image.h b/core/image.h
index 3792103a87..8c4854e053 100644
--- a/core/image.h
+++ b/core/image.h
@@ -108,6 +108,8 @@ public:
INTERPOLATE_NEAREST,
INTERPOLATE_BILINEAR,
INTERPOLATE_CUBIC,
+ INTERPOLATE_TRILINEAR,
+ /* INTERPOLATE_TRICUBIC, */
/* INTERPOLATE GAUSS */
};
diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp
index cff19f990c..e2e71dda92 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -245,7 +245,6 @@ Vector3 AABB::get_longest_axis() const {
if (size.z > max_size) {
axis = Vector3(0, 0, 1);
- max_size = size.z;
}
return axis;
@@ -262,7 +261,6 @@ int AABB::get_longest_axis_index() const {
if (size.z > max_size) {
axis = 2;
- max_size = size.z;
}
return axis;
@@ -280,7 +278,6 @@ Vector3 AABB::get_shortest_axis() const {
if (size.z < max_size) {
axis = Vector3(0, 0, 1);
- max_size = size.z;
}
return axis;
@@ -297,7 +294,6 @@ int AABB::get_shortest_axis_index() const {
if (size.z < max_size) {
axis = 2;
- max_size = size.z;
}
return axis;
diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp
index b1424e1d78..2e184f7a88 100644
--- a/core/math/bsp_tree.cpp
+++ b/core/math/bsp_tree.cpp
@@ -244,10 +244,8 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const {
const Node *nodesptr = &nodes[0];
const Plane *planesptr = &planes[0];
- int plane_count = planes.size();
int idx = node_count - 1;
- int steps = 0;
while (true) {
@@ -259,21 +257,19 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const {
return true;
}
- uint16_t plane = nodesptr[idx].plane;
#ifdef DEBUG_ENABLED
-
+ int plane_count = planes.size();
+ uint16_t plane = nodesptr[idx].plane;
ERR_FAIL_INDEX_V(plane, plane_count, false);
#endif
+
bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point);
idx = over ? nodes[idx].over : nodes[idx].under;
#ifdef DEBUG_ENABLED
-
ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false);
#endif
-
- steps++;
}
return false;
diff --git a/core/translation.cpp b/core/translation.cpp
index aaa4de5912..78115c3749 100644
--- a/core/translation.cpp
+++ b/core/translation.cpp
@@ -1171,13 +1171,11 @@ void TranslationServer::_bind_methods() {
void TranslationServer::load_translations() {
String locale = get_locale();
- bool found = _load_translations("locale/translations"); //all
+ _load_translations("locale/translations"); //all
+ _load_translations("locale/translations_" + locale.substr(0, 2));
- if (_load_translations("locale/translations_" + locale.substr(0, 2)))
- found = true;
if (locale.substr(0, 2) != locale) {
- if (_load_translations("locale/translations_" + locale))
- found = true;
+ _load_translations("locale/translations_" + locale);
}
}
diff --git a/core/vector.h b/core/vector.h
index f586471e27..c026448ddd 100644
--- a/core/vector.h
+++ b/core/vector.h
@@ -152,6 +152,8 @@ public:
Error insert(int p_pos, const T &p_val);
+ void append_array(const Vector<T> &p_other);
+
template <class C>
void sort_custom() {
@@ -408,6 +410,17 @@ Error Vector<T>::insert(int p_pos, const T &p_val) {
}
template <class T>
+void Vector<T>::append_array(const Vector<T> &p_other) {
+ const int ds = p_other.size();
+ if (ds == 0)
+ return;
+ const int bs = size();
+ resize(bs + ds);
+ for (int i = 0; i < ds; ++i)
+ operator[](bs + i) = p_other[i];
+}
+
+template <class T>
Vector<T>::Vector(const Vector &p_from) {
_ptr = NULL;
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index e10485a6b2..55693bd49c 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -593,6 +593,12 @@
</constant>
<constant name="INTERPOLATE_CUBIC" value="2" enum="Interpolation">
</constant>
+ <constant name="INTERPOLATE_TRILINEAR" value="3" enum="Interpolation">
+ Performs bilinear separately on the two most suited mipmap levels, then linearly interpolates between them.
+ It's slower than [code]INTERPOLATE_BILINEAR[/code], but produces higher quality results, with much less aliasing artifacts.
+ If the image does not have mipmaps, they will be generated and used internally, but no mipmaps will be generated on the resulting image. (Note that if you intend to scale multiple copies of the original image, it's better to call [code]generate_mipmaps[/code] on it in advance, to avoid wasting processing power in generating them again and again.)
+ On the other hand, if the image already has mipmaps, they will be used, and a new set will be generated for the resulting image.
+ </constant>
<constant name="ALPHA_NONE" value="0" enum="AlphaMode">
</constant>
<constant name="ALPHA_BIT" value="1" enum="AlphaMode">
diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp
index 6d3e176bbb..b099019d17 100644
--- a/drivers/dummy/texture_loader_dummy.cpp
+++ b/drivers/dummy/texture_loader_dummy.cpp
@@ -45,10 +45,6 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi
dstbuff.resize(rowsize * height);
- PoolVector<uint8_t>::Write dstbuff_write = dstbuff.write();
-
- uint8_t *data = dstbuff_write.ptr();
-
uint8_t **row_p = memnew_arr(uint8_t *, height);
for (unsigned int i = 0; i < height; i++) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index e67b0bea21..9e389a353e 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -3240,7 +3240,7 @@ void RasterizerStorageGLES3::mesh_surface_update_region(RID p_mesh, int p_surfac
PoolVector<uint8_t>::Read r = p_data.read();
- glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->array_id);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id);
glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr());
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
@@ -3404,6 +3404,7 @@ Vector<PoolVector<uint8_t> > RasterizerStorageGLES3::mesh_surface_get_blend_shap
return bsarr;
}
+
Vector<AABB> RasterizerStorageGLES3::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const {
const Mesh *mesh = mesh_owner.getornull(p_mesh);
@@ -3455,6 +3456,7 @@ void RasterizerStorageGLES3::mesh_remove_surface(RID p_mesh, int p_surface) {
mesh->instance_change_notify();
}
+
int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const {
const Mesh *mesh = mesh_owner.getornull(p_mesh);
@@ -3468,6 +3470,7 @@ void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb
ERR_FAIL_COND(!mesh);
mesh->custom_aabb = p_aabb;
+ mesh->instance_change_notify();
}
AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const {
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 80df21941b..1db577f23c 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -693,7 +693,6 @@ public:
AABB custom_aabb;
mutable uint64_t last_pass;
SelfList<MultiMesh>::List multimeshes;
-
_FORCE_INLINE_ void update_multimeshes() {
SelfList<MultiMesh> *mm = multimeshes.first();
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 3622e48ecd..864b9714a9 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -403,7 +403,6 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const {
void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
- int ctr = 0;
// If eol is set to a positive number, you're at the end of the list
if (eol > 0) {
diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp
index db108f9c6b..a1002ef4f9 100644
--- a/drivers/xaudio2/audio_driver_xaudio2.cpp
+++ b/drivers/xaudio2/audio_driver_xaudio2.cpp
@@ -97,8 +97,6 @@ void AudioDriverXAudio2::thread_func(void *p_udata) {
AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata;
- uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate)) * 1000000;
-
while (!ad->exit_thread) {
if (!ad->active) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index ff97878e71..a68a0779c5 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -156,7 +156,6 @@ void EditorNode::_update_scene_tabs() {
scene_tabs->set_current_tab(editor_data.get_edited_scene());
- int current = editor_data.get_edited_scene();
if (scene_tabs->get_offset_buttons_visible()) {
// move add button to fixed position on the tabbar
if (scene_tab_add->get_parent() == scene_tabs) {
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index ea9f6db61a..18cc52a5c6 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -237,8 +237,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
ImageLoaderSVG::set_convert_colors(NULL);
clock_t end_time = clock();
-
- double time_d = (double)(end_time - begin_time) / CLOCKS_PER_SEC;
#else
print_line("Sorry no icons for you");
#endif
@@ -257,7 +255,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
String preset = EDITOR_DEF("interface/theme/preset", "Default");
- int icon_font_color_setting = EDITOR_DEF("interface/theme/icon_and_font_color", 0);
bool highlight_tabs = EDITOR_DEF("interface/theme/highlight_tabs", false);
int border_size = EDITOR_DEF("interface/theme/border_size", 1);
diff --git a/editor/icons/icon_soft_body.svg b/editor/icons/icon_soft_body.svg
new file mode 100644
index 0000000000..9930026b61
--- /dev/null
+++ b/editor/icons/icon_soft_body.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="icon_soft_body.svg">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1027"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="18.792233"
+ inkscape:cx="2.8961304"
+ inkscape:cy="4.3816933"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <path
+ style="opacity:1;fill:#fc9c9c;fill-opacity:0.99607843;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 2.3447105,1.6091897 c -0.011911,1.9816766 -1.4168958,3.9344766 0,5.9495986 1.4168957,2.0151221 0,6.6693597 0,6.6693597 l 10.9510055,0 c 0,0 1.780829,-4.4523824 0,-6.489075 -1.780829,-2.0366925 -0.183458,-4.119112 0,-6.1298833 z m 1.8894067,0.7549031 7.4390658,0 c -0.431995,1.5996085 -1.62289,4.0426807 0,5.3749802 1.622888,1.3322996 0,5.887932 0,5.887932 l -7.4390658,0 c 0,0 1.3903413,-4.3680495 0,-5.9780743 -1.3903412,-1.6100247 -0.3951213,-3.7149271 0,-5.2848379 z"
+ id="rect4142"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="czcczcccczcczc" />
+</svg>
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 876da7f61a..de57a4ad51 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2239,8 +2239,6 @@ void ScriptEditor::_make_script_list_context_menu() {
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE);
}
- EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(selected));
-
context_menu->add_separator();
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 637926a913..af882f6e05 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -60,6 +60,7 @@ public:
virtual Variant get_handle_value(int p_idx) const;
virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+ virtual bool is_gizmo_handle_highlighted(int idx) const { return false; }
virtual bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
virtual bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 0d06b71420..af7c4bb379 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -72,16 +72,26 @@ private:
MESSAGE_SUCCESS
};
+ enum InputType {
+ PROJECT_PATH,
+ INSTALL_PATH
+ };
+
Mode mode;
Button *browse;
+ Button *install_browse;
Button *create_dir;
Container *name_container;
Container *path_container;
+ Container *install_path_container;
Label *msg;
LineEdit *project_path;
LineEdit *project_name;
+ LineEdit *install_path;
TextureRect *status_rect;
+ TextureRect *install_status_rect;
FileDialog *fdialog;
+ FileDialog *fdialog_install;
String zip_path;
String zip_title;
AcceptDialog *dialog_error;
@@ -89,10 +99,11 @@ private:
String created_folder_path;
- void set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS) {
+ void set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS, InputType input_type = PROJECT_PATH) {
msg->set_text(p_msg);
- Ref<Texture> current_icon = status_rect->get_texture();
+ Ref<Texture> current_path_icon = status_rect->get_texture();
+ Ref<Texture> current_install_icon = install_status_rect->get_texture();
Ref<Texture> new_icon;
switch (p_type) {
@@ -119,8 +130,11 @@ private:
} break;
}
- if (current_icon != new_icon)
+ if (current_path_icon != new_icon && input_type == PROJECT_PATH) {
status_rect->set_texture(new_icon);
+ } else if (current_install_icon != new_icon && input_type == INSTALL_PATH) {
+ install_status_rect->set_texture(new_icon);
+ }
set_size(Size2(500, 0) * EDSCALE);
}
@@ -128,11 +142,19 @@ private:
String _test_path() {
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- String valid_path;
+ String valid_path, valid_install_path;
if (d->change_dir(project_path->get_text()) == OK) {
valid_path = project_path->get_text();
} else if (d->change_dir(project_path->get_text().strip_edges()) == OK) {
valid_path = project_path->get_text().strip_edges();
+ } else if (project_path->get_text().ends_with(".zip")) {
+ if (d->file_exists(project_path->get_text())) {
+ valid_path = project_path->get_text();
+ }
+ } else if (project_path->get_text().strip_edges().ends_with(".zip")) {
+ if (d->file_exists(project_path->get_text().strip_edges())) {
+ valid_path = project_path->get_text().strip_edges();
+ }
}
if (valid_path == "") {
@@ -142,11 +164,94 @@ private:
return "";
}
+ if (mode == MODE_IMPORT && valid_path.ends_with(".zip")) {
+ if (d->change_dir(install_path->get_text()) == OK) {
+ valid_install_path = install_path->get_text();
+ } else if (d->change_dir(install_path->get_text().strip_edges()) == OK) {
+ valid_install_path = install_path->get_text().strip_edges();
+ }
+
+ if (valid_install_path == "") {
+ set_message(TTR("The path does not exist."), MESSAGE_ERROR, INSTALL_PATH);
+ memdelete(d);
+ get_ok()->set_disabled(true);
+ return "";
+ }
+ }
+
if (mode == MODE_IMPORT || mode == MODE_RENAME) {
if (valid_path != "" && !d->file_exists("project.godot")) {
- set_message(TTR("Please choose a 'project.godot' file."), MESSAGE_ERROR);
+ if (valid_path.ends_with(".zip")) {
+ FileAccess *src_f = NULL;
+ zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+
+ unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
+ if (!pkg) {
+
+ set_message(TTR("Error opening package file, not in zip format."), MESSAGE_ERROR);
+ memdelete(d);
+ get_ok()->set_disabled(true);
+ unzClose(pkg);
+ return "";
+ }
+
+ int ret = unzGoToFirstFile(pkg);
+ while (ret == UNZ_OK) {
+ unz_file_info info;
+ char fname[16384];
+ ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
+
+ if (String(fname).ends_with("project.godot")) {
+ break;
+ }
+
+ ret = unzGoToNextFile(pkg);
+ }
+
+ if (ret == UNZ_END_OF_LIST_OF_FILE) {
+ set_message(TTR("Invalid '.zip' project file, does not contain a 'project.godot' file."), MESSAGE_ERROR);
+ memdelete(d);
+ get_ok()->set_disabled(true);
+ unzClose(pkg);
+ return "";
+ }
+
+ unzClose(pkg);
+
+ // check if the specified install folder is empty, even though this is not an error, it is good to check here
+ d->list_dir_begin();
+ bool is_empty = true;
+ String n = d->get_next();
+ while (n != String()) {
+ if (n != "." && n != "..") {
+ is_empty = false;
+ break;
+ }
+ n = d->get_next();
+ }
+ d->list_dir_end();
+
+ if (!is_empty) {
+
+ set_message(TTR("Please choose an empty folder."), MESSAGE_WARNING, INSTALL_PATH);
+ memdelete(d);
+ get_ok()->set_disabled(true);
+ return "";
+ }
+
+ } else {
+ set_message(TTR("Please choose a 'project.godot' or '.zip' file."), MESSAGE_ERROR);
+ memdelete(d);
+ install_path_container->hide();
+ get_ok()->set_disabled(true);
+ return "";
+ }
+
+ } else if (valid_path.ends_with("zip")) {
+
+ set_message(TTR("Directory already contains a Godot project."), MESSAGE_ERROR, INSTALL_PATH);
memdelete(d);
get_ok()->set_disabled(true);
return "";
@@ -159,7 +264,7 @@ private:
bool is_empty = true;
String n = d->get_next();
while (n != String()) {
- if (!n.begins_with(".")) { // i don't know if this is enough to guarantee an empty dir
+ if (n != "." && n != "..") { // i don't know if this is enough to guarantee an empty dir
is_empty = false;
break;
}
@@ -177,6 +282,7 @@ private:
}
set_message("");
+ set_message("", MESSAGE_SUCCESS, INSTALL_PATH);
memdelete(d);
get_ok()->set_disabled(false);
return valid_path;
@@ -214,9 +320,14 @@ private:
if (mode == MODE_IMPORT) {
if (p.ends_with("project.godot")) {
p = p.get_base_dir();
+ install_path_container->hide();
+ get_ok()->set_disabled(false);
+ } else if (p.ends_with(".zip")) {
+ install_path->set_text(p.get_base_dir());
+ install_path_container->show();
get_ok()->set_disabled(false);
} else {
- set_message(TTR("Please choose a 'project.godot' file."), MESSAGE_ERROR);
+ set_message(TTR("Please choose a 'project.godot' or '.zip' file."), MESSAGE_ERROR);
get_ok()->set_disabled(true);
return;
}
@@ -224,7 +335,11 @@ private:
String sp = p.simplify_path();
project_path->set_text(sp);
_path_text_changed(sp);
- get_ok()->call_deferred("grab_focus");
+ if (p.ends_with(".zip")) {
+ install_path->call_deferred("grab_focus");
+ } else {
+ get_ok()->call_deferred("grab_focus");
+ }
}
void _path_selected(const String &p_path) {
@@ -236,6 +351,14 @@ private:
get_ok()->call_deferred("grab_focus");
}
+ void _install_path_selected(const String &p_path) {
+ String p = p_path;
+ String sp = p.simplify_path();
+ install_path->set_text(sp);
+ _path_text_changed(sp);
+ get_ok()->call_deferred("grab_focus");
+ }
+
void _browse_path() {
fdialog->set_current_dir(project_path->get_text());
@@ -245,12 +368,19 @@ private:
fdialog->set_mode(FileDialog::MODE_OPEN_FILE);
fdialog->clear_filters();
fdialog->add_filter("project.godot ; " VERSION_NAME " Project");
+ fdialog->add_filter("*.zip ; Zip File");
} else {
fdialog->set_mode(FileDialog::MODE_OPEN_DIR);
}
fdialog->popup_centered_ratio();
}
+ void _browse_install_path() {
+ fdialog_install->set_current_dir(install_path->get_text());
+ fdialog_install->set_mode(FileDialog::MODE_OPEN_DIR);
+ fdialog_install->popup_centered_ratio();
+ }
+
void _create_folder() {
if (project_name->get_text() == "" || created_folder_path != "" || project_name->get_text().ends_with(".") || project_name->get_text().ends_with(" ")) {
@@ -328,7 +458,15 @@ private:
} else {
if (mode == MODE_IMPORT) {
- // nothing to do
+
+ if (project_path->get_text().ends_with(".zip")) {
+
+ mode = MODE_INSTALL;
+ ok_pressed();
+
+ return;
+ }
+
} else {
if (mode == MODE_NEW) {
@@ -357,6 +495,11 @@ private:
} else if (mode == MODE_INSTALL) {
+ if (project_path->get_text().ends_with(".zip")) {
+ dir = install_path->get_text();
+ zip_path = project_path->get_text();
+ }
+
FileAccess *src_f = NULL;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
@@ -444,7 +587,7 @@ private:
dialog_error->set_text(msg);
dialog_error->popup_centered_minsize();
- } else {
+ } else if (!project_path->get_text().ends_with(".zip")) {
dialog_error->set_text(TTR("Package Installed Successfully!"));
dialog_error->popup_centered_minsize();
}
@@ -486,6 +629,9 @@ private:
if (status_rect->get_texture() == get_icon("StatusError", "EditorIcons"))
msg->show();
+
+ if (install_status_rect->get_texture() == get_icon("StatusError", "EditorIcons"))
+ msg->show();
}
void _notification(int p_what) {
@@ -503,6 +649,8 @@ protected:
ClassDB::bind_method("_path_text_changed", &ProjectDialog::_path_text_changed);
ClassDB::bind_method("_path_selected", &ProjectDialog::_path_selected);
ClassDB::bind_method("_file_selected", &ProjectDialog::_file_selected);
+ ClassDB::bind_method("_install_path_selected", &ProjectDialog::_install_path_selected);
+ ClassDB::bind_method("_browse_install_path", &ProjectDialog::_browse_install_path);
ADD_SIGNAL(MethodInfo("project_created"));
ADD_SIGNAL(MethodInfo("project_renamed"));
}
@@ -530,12 +678,15 @@ public:
project_path->set_editable(false);
browse->hide();
+ install_browse->hide();
set_title(TTR("Rename Project"));
get_ok()->set_text(TTR("Rename"));
name_container->show();
status_rect->hide();
msg->hide();
+ install_path_container->hide();
+ install_status_rect->hide();
get_ok()->set_disabled(false);
ProjectSettings *current = memnew(ProjectSettings);
@@ -575,14 +726,18 @@ public:
project_path->set_editable(true);
browse->set_disabled(false);
browse->show();
+ install_browse->set_disabled(false);
+ install_browse->show();
create_dir->show();
status_rect->show();
+ install_status_rect->show();
msg->show();
if (mode == MODE_IMPORT) {
set_title(TTR("Import Existing Project"));
get_ok()->set_text(TTR("Import & Edit"));
name_container->hide();
+ install_path_container->hide();
project_path->grab_focus();
} else if (mode == MODE_NEW) {
@@ -590,6 +745,7 @@ public:
set_title(TTR("Create New Project"));
get_ok()->set_text(TTR("Create & Edit"));
name_container->show();
+ install_path_container->hide();
project_name->grab_focus();
} else if (mode == MODE_INSTALL) {
@@ -597,6 +753,7 @@ public:
set_title(TTR("Install Project:") + " " + zip_title);
get_ok()->set_text(TTR("Install & Edit"));
name_container->hide();
+ install_path_container->hide();
project_path->grab_focus();
}
@@ -644,6 +801,20 @@ public:
project_path->set_h_size_flags(SIZE_EXPAND_FILL);
pphb->add_child(project_path);
+ install_path_container = memnew(VBoxContainer);
+ vb->add_child(install_path_container);
+
+ l = memnew(Label);
+ l->set_text(TTR("Project Installation Path:"));
+ install_path_container->add_child(l);
+
+ HBoxContainer *iphb = memnew(HBoxContainer);
+ install_path_container->add_child(iphb);
+
+ install_path = memnew(LineEdit);
+ install_path->set_h_size_flags(SIZE_EXPAND_FILL);
+ iphb->add_child(install_path);
+
// status icon
status_rect = memnew(TextureRect);
status_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
@@ -654,17 +825,33 @@ public:
browse->connect("pressed", this, "_browse_path");
pphb->add_child(browse);
+ // install status icon
+ install_status_rect = memnew(TextureRect);
+ install_status_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ iphb->add_child(install_status_rect);
+
+ install_browse = memnew(Button);
+ install_browse->set_text(TTR("Browse"));
+ install_browse->connect("pressed", this, "_browse_install_path");
+ iphb->add_child(install_browse);
+
msg = memnew(Label);
msg->set_align(Label::ALIGN_CENTER);
vb->add_child(msg);
fdialog = memnew(FileDialog);
fdialog->set_access(FileDialog::ACCESS_FILESYSTEM);
+ fdialog_install = memnew(FileDialog);
+ fdialog_install->set_access(FileDialog::ACCESS_FILESYSTEM);
add_child(fdialog);
+ add_child(fdialog_install);
project_name->connect("text_changed", this, "_text_changed");
project_path->connect("text_changed", this, "_path_text_changed");
+ install_path->connect("text_changed", this, "_path_text_changed");
fdialog->connect("dir_selected", this, "_path_selected");
fdialog->connect("file_selected", this, "_file_selected");
+ fdialog_install->connect("dir_selected", this, "_install_path_selected");
+ fdialog_install->connect("file_selected", this, "_install_path_selected");
set_hide_on_ok(false);
mode = MODE_NEW;
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index c45dea0df7..35544f711b 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -33,6 +33,7 @@
#include "geometry.h"
#include "quick_hull.h"
#include "scene/3d/camera.h"
+#include "scene/3d/soft_body.h"
#include "scene/resources/box_shape.h"
#include "scene/resources/capsule_shape.h"
#include "scene/resources/convex_polygon_shape.h"
@@ -256,8 +257,12 @@ void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_bi
for (int i = 0; i < p_handles.size(); i++) {
Color col(1, 1, 1, 1);
+ if (is_gizmo_handle_highlighted(i))
+ col = Color(0, 0, 1, 0.9);
+
if (SpatialEditor::get_singleton()->get_over_gizmo_handle() != i)
- col = Color(0.9, 0.9, 0.9, 0.9);
+ col.a = 0.8;
+
w[i] = col;
}
}
@@ -1914,6 +1919,100 @@ VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel *p_car_wheel) {
///////////
+void SoftBodySpatialGizmo::redraw() {
+ clear();
+
+ if (!soft_body || soft_body->get_mesh().is_null()) {
+ return;
+ }
+
+ // find mesh
+
+ Vector<Vector3> lines;
+
+ soft_body->get_mesh()->generate_debug_mesh_lines(lines);
+
+ if (!lines.size()) {
+ return;
+ }
+
+ Vector<Vector3> points;
+ soft_body->get_mesh()->generate_debug_mesh_indices(points);
+
+ soft_body->get_mesh()->clear_cache();
+
+ Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape");
+ Ref<Material> material = create_material("shape_material", gizmo_color);
+
+ add_lines(lines, material);
+ add_collision_segments(lines);
+ add_handles(points);
+}
+
+bool SoftBodySpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) {
+ return EditorSpatialGizmo::intersect_ray(p_camera, p_point, r_pos, r_normal, r_gizmo_handle, p_sec_first);
+
+ /* Perform a shape cast but doesn't work with softbody
+ PhysicsDirectSpaceState *space_state = PhysicsServer::get_singleton()->space_get_direct_state(SceneTree::get_singleton()->get_root()->get_world()->get_space());
+ if (!physics_sphere_shape.is_valid()) {
+ physics_sphere_shape = PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_SPHERE);
+ real_t radius = 0.02;
+ PhysicsServer::get_singleton()->shape_set_data(physics_sphere_shape, radius);
+ }
+
+ Vector3 sphere_motion(p_camera->project_ray_normal(p_point));
+ real_t closest_safe;
+ real_t closest_unsafe;
+ PhysicsDirectSpaceState::ShapeRestInfo result;
+ bool collided = space_state->cast_motion(
+ physics_sphere_shape,
+ p_camera->get_transform(),
+ sphere_motion * Vector3(1000, 1000, 1000),
+ 0.f,
+ closest_safe,
+ closest_unsafe,
+ Set<RID>(),
+ 0xFFFFFFFF,
+ 0xFFFFFFFF,
+ &result);
+
+ if (collided) {
+
+ if (result.collider_id == soft_body->get_instance_id()) {
+ print_line("Collided");
+ } else {
+ print_line("Collided but with wrong object: " + itos(result.collider_id));
+ }
+ } else {
+ print_line("Not collided, motion: x: " + rtos(sphere_motion[0]) + " y: " + rtos(sphere_motion[1]) + " z: " + rtos(sphere_motion[2]));
+ }
+ return false;
+ */
+}
+
+void SoftBodySpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+ soft_body->pin_point_toggle(p_idx);
+ redraw();
+}
+
+bool SoftBodySpatialGizmo::is_gizmo_handle_highlighted(int idx) const {
+ return soft_body->is_point_pinned(idx);
+}
+
+SoftBodySpatialGizmo::SoftBodySpatialGizmo(SoftBody *p_soft_physics_body) :
+ EditorSpatialGizmo(),
+ soft_body(p_soft_physics_body) {
+ set_spatial_node(p_soft_physics_body);
+}
+
+SoftBodySpatialGizmo::~SoftBodySpatialGizmo() {
+ //if (!physics_sphere_shape.is_valid()) {
+ // PhysicsServer::get_singleton()->free(physics_sphere_shape);
+ //}
+}
+
+///////////
+
String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const {
Ref<Shape> s = cs->get_shape();
@@ -4051,6 +4150,12 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
return lsg;
}
+ if (Object::cast_to<SoftBody>(p_spatial)) {
+
+ Ref<SoftBodySpatialGizmo> misg = memnew(SoftBodySpatialGizmo(Object::cast_to<SoftBody>(p_spatial)));
+ return misg;
+ }
+
if (Object::cast_to<MeshInstance>(p_spatial)) {
Ref<MeshInstanceSpatialGizmo> misg = memnew(MeshInstanceSpatialGizmo(Object::cast_to<MeshInstance>(p_spatial)));
@@ -4081,6 +4186,7 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
return misg;
}
*/
+
if (Object::cast_to<CollisionShape>(p_spatial)) {
Ref<CollisionShapeSpatialGizmo> misg = memnew(CollisionShapeSpatialGizmo(Object::cast_to<CollisionShape>(p_spatial)));
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index 924f82dc16..198d028516 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -331,6 +331,23 @@ public:
BakedIndirectLightGizmo(BakedLightmap *p_baker = NULL);
};
+class SoftBodySpatialGizmo : public EditorSpatialGizmo {
+ GDCLASS(SoftBodySpatialGizmo, EditorSpatialGizmo);
+
+ class SoftBody *soft_body;
+ //RID physics_sphere_shape; // Used for raycast that doesn't work, in this moment, with softbody
+
+public:
+ void redraw();
+ virtual bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel);
+
+ virtual bool is_gizmo_handle_highlighted(int idx) const;
+
+ SoftBodySpatialGizmo(SoftBody *p_soft_physics_body = NULL);
+ ~SoftBodySpatialGizmo();
+};
+
class CollisionShapeSpatialGizmo : public EditorSpatialGizmo {
GDCLASS(CollisionShapeSpatialGizmo, EditorSpatialGizmo);
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 77484c9efc..2390c71b0a 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -169,7 +169,7 @@ real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const {
}
RID BulletPhysicsServer::space_create() {
- SpaceBullet *space = bulletnew(SpaceBullet(false));
+ SpaceBullet *space = bulletnew(SpaceBullet);
CreateThenReturnRID(space_owner, space);
}
@@ -567,9 +567,6 @@ void BulletPhysicsServer::body_clear_shapes(RID p_body) {
void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) {
CollisionObjectBullet *body = get_collisin_object(p_body);
- if (!body) {
- body = soft_body_owner.get(p_body);
- }
ERR_FAIL_COND(!body);
body->set_instance_id(p_ID);
@@ -867,6 +864,13 @@ RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) {
CreateThenReturnRID(soft_body_owner, body);
}
+void BulletPhysicsServer::soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->update_visual_server(p_visual_server_handler);
+}
+
void BulletPhysicsServer::soft_body_set_space(RID p_body, RID p_space) {
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -893,11 +897,11 @@ RID BulletPhysicsServer::soft_body_get_space(RID p_body) const {
return space->get_self();
}
-void BulletPhysicsServer::soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+void BulletPhysicsServer::soft_body_set_mesh(RID p_body, const REF &p_mesh) {
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
- body->set_trimesh_body_shape(p_indices, p_vertices, p_triangles_num);
+ body->set_soft_mesh(p_mesh);
}
void BulletPhysicsServer::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {
@@ -975,14 +979,16 @@ void BulletPhysicsServer::soft_body_set_transform(RID p_body, const Transform &p
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
- body->set_transform(p_transform);
+ body->set_soft_transform(p_transform);
}
-Transform BulletPhysicsServer::soft_body_get_transform(RID p_body) const {
+Vector3 BulletPhysicsServer::soft_body_get_vertex_position(RID p_body, int vertex_index) const {
const SoftBodyBullet *body = soft_body_owner.get(p_body);
- ERR_FAIL_COND_V(!body, Transform());
+ Vector3 pos;
+ ERR_FAIL_COND_V(!body, pos);
- return body->get_transform();
+ body->get_node_position(vertex_index, pos);
+ return pos;
}
void BulletPhysicsServer::soft_body_set_ray_pickable(RID p_body, bool p_enable) {
@@ -997,6 +1003,154 @@ bool BulletPhysicsServer::soft_body_is_ray_pickable(RID p_body) const {
return body->is_ray_pickable();
}
+void BulletPhysicsServer::soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_simulation_precision(p_simulation_precision);
+}
+
+int BulletPhysicsServer::soft_body_get_simulation_precision(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_simulation_precision();
+}
+
+void BulletPhysicsServer::soft_body_set_total_mass(RID p_body, real_t p_total_mass) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_total_mass(p_total_mass);
+}
+
+real_t BulletPhysicsServer::soft_body_get_total_mass(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_total_mass();
+}
+
+void BulletPhysicsServer::soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_linear_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_linear_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_linear_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_areaAngular_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_areaAngular_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_areaAngular_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_volume_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_volume_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_volume_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_pressure_coefficient(p_pressure_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_pressure_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_pressure_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ return body->set_pose_matching_coefficient(p_pose_matching_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_pose_matching_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_pose_matching_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_damping_coefficient(p_damping_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_damping_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_damping_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_drag_coefficient(p_drag_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_drag_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_drag_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_node_position(p_point_index, p_global_position);
+}
+
+Vector3 BulletPhysicsServer::soft_body_get_point_global_position(RID p_body, int p_point_index) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3(0., 0., 0.));
+ Vector3 pos;
+ body->get_node_position(p_point_index, pos);
+ return pos;
+}
+
+Vector3 BulletPhysicsServer::soft_body_get_point_offset(RID p_body, int p_point_index) const {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3());
+ Vector3 res;
+ body->get_node_offset(p_point_index, res);
+ return res;
+}
+
+void BulletPhysicsServer::soft_body_remove_all_pinned_points(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->reset_all_node_mass();
+}
+
+void BulletPhysicsServer::soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_node_mass(p_point_index, p_pin ? 0 : 1);
+}
+
+bool BulletPhysicsServer::soft_body_is_point_pinned(RID p_body, int p_point_index) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_node_mass(p_point_index);
+}
+
PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const {
JointBullet *joint = joint_owner.get(p_joint);
ERR_FAIL_COND_V(!joint, JOINT_PIN);
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 06769106c3..2165845529 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -262,10 +262,12 @@ public:
virtual RID soft_body_create(bool p_init_sleeping = false);
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler);
+
virtual void soft_body_set_space(RID p_body, RID p_space);
virtual RID soft_body_get_space(RID p_body) const;
- virtual void soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh);
virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer);
virtual uint32_t soft_body_get_collision_layer(RID p_body) const;
@@ -280,12 +282,49 @@ public:
virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const;
+ /// Special function. This function has bad performance
virtual void soft_body_set_transform(RID p_body, const Transform &p_transform);
- virtual Transform soft_body_get_transform(RID p_body) const;
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const;
virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable);
virtual bool soft_body_is_ray_pickable(RID p_body) const;
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision);
+ virtual int soft_body_get_simulation_precision(RID p_body);
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass);
+ virtual real_t soft_body_get_total_mass(RID p_body);
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_linear_stiffness(RID p_body);
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body);
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_volume_stiffness(RID p_body);
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient);
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body);
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient);
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body);
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient);
+ virtual real_t soft_body_get_damping_coefficient(RID p_body);
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient);
+ virtual real_t soft_body_get_drag_coefficient(RID p_body);
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position);
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index);
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const;
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body);
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin);
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index);
+
/* JOINT API */
virtual JointType joint_get_type(RID p_joint) const;
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 57e4db708e..1d63318fd7 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -111,6 +111,8 @@ void CollisionObjectBullet::setupBulletCollisionObject(btCollisionObject *p_coll
void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
exceptions.insert(p_ignoreCollisionObject->get_self());
+ if (!bt_collision_object)
+ return;
bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, true);
if (space)
space->get_broadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bt_collision_object->getBroadphaseHandle(), space->get_dispatcher());
diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp
index b75f7464ab..a76b0438b4 100644
--- a/modules/bullet/register_types.cpp
+++ b/modules/bullet/register_types.cpp
@@ -32,6 +32,7 @@
#include "bullet_physics_server.h"
#include "class_db.h"
+#include "project_settings.h"
/**
@author AndreaCatania
@@ -47,6 +48,9 @@ void register_bullet_types() {
#ifndef _3D_DISABLED
PhysicsServerManager::register_server("Bullet", &_createBulletPhysicsCallback);
PhysicsServerManager::set_default_server("Bullet", 1);
+
+ GLOBAL_DEF("physics/3d/active_soft_world", true);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/active_soft_world", PropertyInfo(Variant::BOOL, "physics/3d/active_soft_world"));
#endif
}
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 5c20eb73f1..b3680d58db 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -32,42 +32,24 @@
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
-#include "scene/3d/immediate_geometry.h"
+#include "scene/3d/soft_body.h"
#include "space_bullet.h"
-/**
- @author AndreaCatania
-*/
-
SoftBodyBullet::SoftBodyBullet() :
CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY),
- mass(1),
+ total_mass(1),
simulation_precision(5),
- stiffness(0.5f),
- pressure_coefficient(50),
- damping_coefficient(0.005),
- drag_coefficient(0.005),
+ linear_stiffness(0.5),
+ areaAngular_stiffness(0.5),
+ volume_stiffness(0.5),
+ pressure_coefficient(0.),
+ pose_matching_coefficient(0.),
+ damping_coefficient(0.01),
+ drag_coefficient(0.),
bt_soft_body(NULL),
- soft_shape_type(SOFT_SHAPETYPE_NONE),
- isScratched(false),
- soft_body_shape_data(NULL) {
-
- test_geometry = memnew(ImmediateGeometry);
-
- red_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial));
- red_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
- red_mat->set_line_width(20.0);
- red_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- red_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- red_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
- red_mat->set_albedo(Color(1, 0, 0, 1));
- test_geometry->set_material_override(red_mat);
-
- test_is_in_scene = false;
-}
+ isScratched(false) {}
SoftBodyBullet::~SoftBodyBullet() {
- bulletdelete(soft_body_shape_data);
}
void SoftBodyBullet::reload_body() {
@@ -80,8 +62,6 @@ void SoftBodyBullet::reload_body() {
void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
isScratched = false;
-
- // Remove this object from the physics world
space->remove_soft_body(this);
}
@@ -90,86 +70,181 @@ void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
space->add_soft_body(this);
}
-
- reload_soft_body();
}
-void SoftBodyBullet::dispatch_callbacks() {
- if (!bt_soft_body) {
+void SoftBodyBullet::dispatch_callbacks() {}
+
+void SoftBodyBullet::on_collision_filters_change() {}
+
+void SoftBodyBullet::on_collision_checker_start() {}
+
+void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {}
+
+void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {}
+
+void SoftBodyBullet::update_visual_server(SoftBodyVisualServerHandler *p_visual_server_handler) {
+ if (!bt_soft_body)
return;
+
+ /// Update visual server vertices
+ const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes);
+ const int nodes_count = nodes.size();
+
+ Vector<int> *vs_indices;
+ const void *vertex_position;
+ const void *vertex_normal;
+
+ for (int vertex_index = 0; vertex_index < nodes_count; ++vertex_index) {
+ vertex_position = reinterpret_cast<const void *>(&nodes[vertex_index].m_x);
+ vertex_normal = reinterpret_cast<const void *>(&nodes[vertex_index].m_n);
+
+ vs_indices = &indices_table[vertex_index];
+
+ const int vs_indices_size(vs_indices->size());
+ for (int x = 0; x < vs_indices_size; ++x) {
+ p_visual_server_handler->set_vertex((*vs_indices)[x], vertex_position);
+ p_visual_server_handler->set_normal((*vs_indices)[x], vertex_normal);
+ }
}
- if (!test_is_in_scene) {
- test_is_in_scene = true;
- SceneTree::get_singleton()->get_current_scene()->add_child(test_geometry);
+ /// Generate AABB
+ btVector3 aabb_min;
+ btVector3 aabb_max;
+ bt_soft_body->getAabb(aabb_min, aabb_max);
+
+ btVector3 size(aabb_max - aabb_min);
+
+ AABB aabb;
+ B_TO_G(aabb_min, aabb.position);
+ B_TO_G(size, aabb.size);
+
+ p_visual_server_handler->set_aabb(aabb);
+}
+
+void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) {
+
+ if (p_mesh.is_null() || !p_mesh->surface_is_softbody_friendly(0))
+ soft_mesh.unref();
+ else
+ soft_mesh = p_mesh;
+
+ if (soft_mesh.is_null()) {
+
+ destroy_soft_body();
+ return;
}
- test_geometry->clear();
- test_geometry->begin(Mesh::PRIMITIVE_LINES, NULL);
- bool first = true;
- Vector3 pos;
- for (int i = 0; i < bt_soft_body->m_nodes.size(); ++i) {
- const btSoftBody::Node &n = bt_soft_body->m_nodes[i];
- B_TO_G(n.m_x, pos);
- test_geometry->add_vertex(pos);
- if (!first) {
- test_geometry->add_vertex(pos);
- } else {
- first = false;
- }
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ ERR_FAIL_COND(!(soft_mesh->surface_get_format(0) & VS::ARRAY_FORMAT_INDEX));
+ set_trimesh_body_shape(arrays[VS::ARRAY_INDEX], arrays[VS::ARRAY_VERTEX]);
+}
+
+void SoftBodyBullet::destroy_soft_body() {
+
+ if (!bt_soft_body)
+ return;
+
+ if (space) {
+ /// Remove from world before deletion
+ space->remove_soft_body(this);
}
- test_geometry->end();
+
+ destroyBulletCollisionObject();
+ bt_soft_body = NULL;
+}
+
+void SoftBodyBullet::set_soft_transform(const Transform &p_transform) {
+ reset_all_node_positions();
+ move_all_nodes(p_transform);
}
-void SoftBodyBullet::on_collision_filters_change() {
+void SoftBodyBullet::move_all_nodes(const Transform &p_transform) {
+ if (!bt_soft_body)
+ return;
+ btTransform bt_transf;
+ G_TO_B(p_transform, bt_transf);
+ bt_soft_body->transform(bt_transf);
}
-void SoftBodyBullet::on_collision_checker_start() {
+void SoftBodyBullet::set_node_position(int p_node_index, const Vector3 &p_global_position) {
+ btVector3 bt_pos;
+ G_TO_B(p_global_position, bt_pos);
+ set_node_position(p_node_index, bt_pos);
}
-void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {
+void SoftBodyBullet::set_node_position(int p_node_index, const btVector3 &p_global_position) {
+ if (bt_soft_body) {
+ bt_soft_body->m_nodes[p_node_index].m_x = p_global_position;
+ }
}
-void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {
+void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) const {
+ if (bt_soft_body) {
+ B_TO_G(bt_soft_body->m_nodes[p_node_index].m_x, r_position);
+ }
}
-void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+void SoftBodyBullet::get_node_offset(int p_node_index, Vector3 &r_offset) const {
+ if (soft_mesh.is_null())
+ return;
+
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ PoolVector<Vector3> vertices(arrays[VS::ARRAY_VERTEX]);
- TrimeshSoftShapeData *shape_data = bulletnew(TrimeshSoftShapeData);
- shape_data->m_triangles_indices = p_indices;
- shape_data->m_vertices = p_vertices;
- shape_data->m_triangles_num = p_triangles_num;
+ if (0 <= p_node_index && vertices.size() > p_node_index) {
+ r_offset = vertices[p_node_index];
+ }
+}
- set_body_shape_data(shape_data, SOFT_SHAPE_TYPE_TRIMESH);
- reload_soft_body();
+void SoftBodyBullet::get_node_offset(int p_node_index, btVector3 &r_offset) const {
+ Vector3 off;
+ get_node_offset(p_node_index, off);
+ G_TO_B(off, r_offset);
}
-void SoftBodyBullet::set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type) {
- bulletdelete(soft_body_shape_data);
- soft_body_shape_data = p_soft_shape_data;
- soft_shape_type = p_type;
+void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) {
+ if (0 >= p_mass) {
+ pin_node(node_index);
+ } else {
+ unpin_node(node_index);
+ }
+ if (bt_soft_body) {
+ bt_soft_body->setMass(node_index, p_mass);
+ }
}
-void SoftBodyBullet::set_transform(const Transform &p_transform) {
- transform = p_transform;
+btScalar SoftBodyBullet::get_node_mass(int node_index) const {
if (bt_soft_body) {
- // TODO the softbody set new transform considering the current transform as center of world
- // like if it's local transform, so I must fix this by setting nwe transform considering the old
- btTransform bt_trans;
- G_TO_B(transform, bt_trans);
- //bt_soft_body->transform(bt_trans);
+ return bt_soft_body->getMass(node_index);
+ } else {
+ return -1 == search_node_pinned(node_index) ? 1 : 0;
}
}
-const Transform &SoftBodyBullet::get_transform() const {
- return transform;
+void SoftBodyBullet::reset_all_node_mass() {
+ if (bt_soft_body) {
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ bt_soft_body->setMass(pinned_nodes[i], 1);
+ }
+ }
+ pinned_nodes.resize(0);
}
-void SoftBodyBullet::get_first_node_origin(btVector3 &p_out_origin) const {
- if (bt_soft_body && bt_soft_body->m_nodes.size()) {
- p_out_origin = bt_soft_body->m_nodes[0].m_x;
- } else {
- p_out_origin.setZero();
+void SoftBodyBullet::reset_all_node_positions() {
+ if (soft_mesh.is_null())
+ return;
+
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ PoolVector<Vector3> vs_vertices(arrays[VS::ARRAY_VERTEX]);
+ PoolVector<Vector3>::Read vs_vertices_read = vs_vertices.read();
+
+ for (int vertex_index = bt_soft_body->m_nodes.size() - 1; 0 <= vertex_index; --vertex_index) {
+
+ G_TO_B(vs_vertices_read[indices_table[vertex_index][0]], bt_soft_body->m_nodes[vertex_index].m_x);
+
+ bt_soft_body->m_nodes[vertex_index].m_q = bt_soft_body->m_nodes[vertex_index].m_x;
+ bt_soft_body->m_nodes[vertex_index].m_v = btVector3(0, 0, 0);
+ bt_soft_body->m_nodes[vertex_index].m_f = btVector3(0, 0, 0);
}
}
@@ -181,22 +256,34 @@ void SoftBodyBullet::set_activation_state(bool p_active) {
}
}
-void SoftBodyBullet::set_mass(real_t p_val) {
+void SoftBodyBullet::set_total_mass(real_t p_val) {
if (0 >= p_val) {
p_val = 1;
}
- mass = p_val;
+ total_mass = p_val;
if (bt_soft_body) {
- bt_soft_body->setTotalMass(mass);
+ bt_soft_body->setTotalMass(total_mass);
}
}
-void SoftBodyBullet::set_stiffness(real_t p_val) {
- stiffness = p_val;
+void SoftBodyBullet::set_linear_stiffness(real_t p_val) {
+ linear_stiffness = p_val;
if (bt_soft_body) {
- mat0->m_kAST = stiffness;
- mat0->m_kLST = stiffness;
- mat0->m_kVST = stiffness;
+ mat0->m_kLST = linear_stiffness;
+ }
+}
+
+void SoftBodyBullet::set_areaAngular_stiffness(real_t p_val) {
+ areaAngular_stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kAST = areaAngular_stiffness;
+ }
+}
+
+void SoftBodyBullet::set_volume_stiffness(real_t p_val) {
+ volume_stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kVST = volume_stiffness;
}
}
@@ -204,6 +291,9 @@ void SoftBodyBullet::set_simulation_precision(int p_val) {
simulation_precision = p_val;
if (bt_soft_body) {
bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.viterations = simulation_precision;
+ bt_soft_body->m_cfg.diterations = simulation_precision;
+ bt_soft_body->m_cfg.citerations = simulation_precision;
}
}
@@ -214,6 +304,13 @@ void SoftBodyBullet::set_pressure_coefficient(real_t p_val) {
}
}
+void SoftBodyBullet::set_pose_matching_coefficient(real_t p_val) {
+ pose_matching_coefficient = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
+ }
+}
+
void SoftBodyBullet::set_damping_coefficient(real_t p_val) {
damping_coefficient = p_val;
if (bt_soft_body) {
@@ -228,89 +325,156 @@ void SoftBodyBullet::set_drag_coefficient(real_t p_val) {
}
}
-void SoftBodyBullet::reload_soft_body() {
-
+void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices) {
+ /// Assert the current soft body is destroyed
destroy_soft_body();
- create_soft_body();
- if (bt_soft_body) {
+ /// Parse visual server indices to physical indices.
+ /// Merge all overlapping vertices and create a map of physical vertices to visual server
- // TODO the softbody set new transform considering the current transform as center of world
- // like if it's local transform, so I must fix this by setting nwe transform considering the old
- btTransform bt_trans;
- G_TO_B(transform, bt_trans);
- bt_soft_body->transform(bt_trans);
+ {
+ /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map
+ Vector<int> vs_indices_to_physics_table;
- bt_soft_body->generateBendingConstraints(2, mat0);
- mat0->m_kAST = stiffness;
- mat0->m_kLST = stiffness;
- mat0->m_kVST = stiffness;
+ { // Map vertices
+ indices_table.resize(0);
- bt_soft_body->m_cfg.piterations = simulation_precision;
- bt_soft_body->m_cfg.kDP = damping_coefficient;
- bt_soft_body->m_cfg.kDG = drag_coefficient;
- bt_soft_body->m_cfg.kPR = pressure_coefficient;
- bt_soft_body->setTotalMass(mass);
- }
- if (space) {
- // TODO remove this please
- space->add_soft_body(this);
- }
-}
+ int index = 0;
+ Map<Vector3, int> unique_vertices;
-void SoftBodyBullet::create_soft_body() {
- if (!space || !soft_body_shape_data) {
- return;
- }
- ERR_FAIL_COND(!space->is_using_soft_world());
- switch (soft_shape_type) {
- case SOFT_SHAPE_TYPE_TRIMESH: {
- TrimeshSoftShapeData *trimesh_data = static_cast<TrimeshSoftShapeData *>(soft_body_shape_data);
-
- Vector<int> indices;
- Vector<btScalar> vertices;
-
- int i;
- const int indices_size = trimesh_data->m_triangles_indices.size();
- const int vertices_size = trimesh_data->m_vertices.size();
- indices.resize(indices_size);
- vertices.resize(vertices_size * 3);
-
- PoolVector<int>::Read i_r = trimesh_data->m_triangles_indices.read();
- for (i = 0; i < indices_size; ++i) {
- indices[i] = i_r[i];
+ const int vs_vertices_size(p_vertices.size());
+
+ PoolVector<Vector3>::Read p_vertices_read = p_vertices.read();
+
+ for (int vs_vertex_index = 0; vs_vertex_index < vs_vertices_size; ++vs_vertex_index) {
+
+ Map<Vector3, int>::Element *e = unique_vertices.find(p_vertices_read[vs_vertex_index]);
+ int vertex_id;
+ if (e) {
+ // Already rxisting
+ vertex_id = e->value();
+ } else {
+ // Create new one
+ unique_vertices[p_vertices_read[vs_vertex_index]] = vertex_id = index++;
+ indices_table.push_back(Vector<int>());
+ }
+
+ indices_table[vertex_id].push_back(vs_vertex_index);
+ vs_indices_to_physics_table.push_back(vertex_id);
+ }
+ }
+
+ const int indices_map_size(indices_table.size());
+
+ Vector<btScalar> bt_vertices;
+
+ { // Parse vertices to bullet
+
+ bt_vertices.resize(indices_map_size * 3);
+ PoolVector<Vector3>::Read p_vertices_read = p_vertices.read();
+
+ for (int i = 0; i < indices_map_size; ++i) {
+ bt_vertices[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
+ bt_vertices[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
+ bt_vertices[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
}
- i_r = PoolVector<int>::Read();
+ }
+
+ Vector<int> bt_triangles;
+ const int triangles_size(p_indices.size() / 3);
+
+ { // Parse indices
+
+ bt_triangles.resize(triangles_size * 3);
+
+ PoolVector<int>::Read p_indices_read = p_indices.read();
- PoolVector<Vector3>::Read f_r = trimesh_data->m_vertices.read();
- for (int j = i = 0; i < vertices_size; ++i, j += 3) {
- vertices[j + 0] = f_r[i][0];
- vertices[j + 1] = f_r[i][1];
- vertices[j + 2] = f_r[i][2];
+ for (int i = 0; i < triangles_size; ++i) {
+ bt_triangles[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
+ bt_triangles[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
+ bt_triangles[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
}
- f_r = PoolVector<Vector3>::Read();
+ }
- bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(*space->get_soft_body_world_info(), vertices.ptr(), indices.ptr(), trimesh_data->m_triangles_num);
- } break;
- default:
- ERR_PRINT("Shape type not supported");
- return;
+ btSoftBodyWorldInfo fake_world_info;
+ bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(fake_world_info, &bt_vertices[0], &bt_triangles[0], triangles_size, false);
+ setup_soft_body();
}
+}
+
+void SoftBodyBullet::setup_soft_body() {
+
+ if (!bt_soft_body)
+ return;
+ // Soft body setup
setupBulletCollisionObject(bt_soft_body);
- bt_soft_body->getCollisionShape()->setMargin(0.001f);
+ bt_soft_body->m_worldInfo = NULL; // Remove fake world info
+ bt_soft_body->getCollisionShape()->setMargin(0.01);
bt_soft_body->setCollisionFlags(bt_soft_body->getCollisionFlags() & (~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT)));
+
+ // Space setup
+ if (space) {
+ space->add_soft_body(this);
+ }
+
mat0 = bt_soft_body->appendMaterial();
+
+ // Assign soft body data
+ bt_soft_body->generateBendingConstraints(2, mat0);
+
+ mat0->m_kLST = linear_stiffness;
+ mat0->m_kAST = areaAngular_stiffness;
+ mat0->m_kVST = volume_stiffness;
+
+ // Clusters allow to have Soft vs Soft collision but doesn't work well right now
+
+ //bt_soft_body->m_cfg.kSRHR_CL = 1;// Soft vs rigid hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSKHR_CL = 1;// Soft vs kinematic hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSSHR_CL = 1;// Soft vs soft hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSR_SPLT_CL = 1; // Soft vs rigid impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSK_SPLT_CL = 1; // Soft vs kinematic impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSS_SPLT_CL = 1; // Soft vs Soft impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.collisions = btSoftBody::fCollision::CL_SS + btSoftBody::fCollision::CL_RS + btSoftBody::fCollision::VF_SS;
+ //bt_soft_body->generateClusters(64);
+
+ bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.viterations = simulation_precision;
+ bt_soft_body->m_cfg.diterations = simulation_precision;
+ bt_soft_body->m_cfg.citerations = simulation_precision;
+ bt_soft_body->m_cfg.kDP = damping_coefficient;
+ bt_soft_body->m_cfg.kDG = drag_coefficient;
+ bt_soft_body->m_cfg.kPR = pressure_coefficient;
+ bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
+ bt_soft_body->setTotalMass(total_mass);
+
+ btSoftBodyHelpers::ReoptimizeLinkOrder(bt_soft_body);
+ bt_soft_body->updateBounds();
+
+ // Set pinned nodes
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ bt_soft_body->setMass(pinned_nodes[i], 0);
+ }
}
-void SoftBodyBullet::destroy_soft_body() {
- if (space) {
- /// This step is required to assert that the body is not into the world during deletion
- /// This step is required since to change the body shape the body must be re-created.
- /// Here is handled the case when the body is assigned into a world and the body
- /// shape is changed.
- space->remove_soft_body(this);
+void SoftBodyBullet::pin_node(int p_node_index) {
+ if (-1 == search_node_pinned(p_node_index)) {
+ pinned_nodes.push_back(p_node_index);
}
- destroyBulletCollisionObject();
- bt_soft_body = NULL;
+}
+
+void SoftBodyBullet::unpin_node(int p_node_index) {
+ const int id = search_node_pinned(p_node_index);
+ if (-1 != id) {
+ pinned_nodes.remove(id);
+ }
+}
+
+int SoftBodyBullet::search_node_pinned(int p_node_index) const {
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ if (p_node_index == pinned_nodes[i]) {
+ return i;
+ }
+ }
+ return -1;
}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index 9895643b84..c775193584 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -40,7 +40,10 @@
#define x11_None 0L
#endif
-#include <BulletSoftBody/btSoftBodyHelpers.h>
+#include "BulletSoftBody/btSoftBodyHelpers.h"
+#include "collision_object_bullet.h"
+#include "scene/resources/mesh.h"
+#include "servers/physics_server.h"
#ifdef x11_None
/// This is required to re add the macro None defined by x11 compiler
@@ -52,39 +55,34 @@
@author AndreaCatania
*/
-struct SoftShapeData {};
-struct TrimeshSoftShapeData : public SoftShapeData {
- PoolVector<int> m_triangles_indices;
- PoolVector<Vector3> m_vertices;
- int m_triangles_num;
-};
-
class SoftBodyBullet : public CollisionObjectBullet {
-public:
- enum SoftShapeType {
- SOFT_SHAPETYPE_NONE = 0,
- SOFT_SHAPE_TYPE_TRIMESH
- };
private:
btSoftBody *bt_soft_body;
+ Vector<Vector<int> > indices_table;
btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
- SoftShapeType soft_shape_type;
bool isScratched;
- SoftShapeData *soft_body_shape_data;
+ Ref<Mesh> soft_mesh;
- Transform transform;
int simulation_precision;
- real_t mass;
- real_t stiffness; // [0,1]
+ real_t total_mass;
+ real_t linear_stiffness; // [0,1]
+ real_t areaAngular_stiffness; // [0,1]
+ real_t volume_stiffness; // [0,1]
real_t pressure_coefficient; // [-inf,+inf]
+ real_t pose_matching_coefficient; // [0,1]
real_t damping_coefficient; // [0,1]
real_t drag_coefficient; // [0,1]
+ Vector<int> pinned_nodes;
- class ImmediateGeometry *test_geometry; // TODO remove this please
- Ref<SpatialMaterial> red_mat; // TODO remove this please
- bool test_is_in_scene; // TODO remove this please
+ // Other property to add
+ //btScalar kVC; // Volume conversation coefficient [0,+inf]
+ //btScalar kDF; // Dynamic friction coefficient [0,1]
+ //btScalar kMT; // Pose matching coefficient [0,1]
+ //btScalar kCHR; // Rigid contacts hardness [0,1]
+ //btScalar kKHR; // Kinetic contacts hardness [0,1]
+ //btScalar kSHR; // Soft contacts hardness [0,1]
public:
SoftBodyBullet();
@@ -101,39 +99,64 @@ public:
_FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
- void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
- void set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type);
+ void update_visual_server(class SoftBodyVisualServerHandler *p_visual_server_handler);
- void set_transform(const Transform &p_transform);
- /// This function doesn't return the exact COM transform.
- /// It returns the origin only of first node (vertice) of current soft body
- /// ---
- /// The soft body doesn't have a fixed center of mass, but is a group of nodes (vertices)
- /// that each has its own position in the world.
- /// For this reason return the correct COM is not so simple and must be calculate
- /// Check this to improve this function http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=8803
- const Transform &get_transform() const;
- void get_first_node_origin(btVector3 &p_out_origin) const;
+ void set_soft_mesh(const Ref<Mesh> &p_mesh);
+ void destroy_soft_body();
+
+ // Special function. This function has bad performance
+ void set_soft_transform(const Transform &p_transform);
+
+ void move_all_nodes(const Transform &p_transform);
+ void set_node_position(int node_index, const Vector3 &p_global_position);
+ void set_node_position(int node_index, const btVector3 &p_global_position);
+ void get_node_position(int node_index, Vector3 &r_position) const;
+ // Heavy function, Please cache this info
+ void get_node_offset(int node_index, Vector3 &r_offset) const;
+ // Heavy function, Please cache this info
+ void get_node_offset(int node_index, btVector3 &r_offset) const;
+
+ void set_node_mass(int node_index, btScalar p_mass);
+ btScalar get_node_mass(int node_index) const;
+ void reset_all_node_mass();
+ void reset_all_node_positions();
void set_activation_state(bool p_active);
- void set_mass(real_t p_val);
- _FORCE_INLINE_ real_t get_mass() const { return mass; }
- void set_stiffness(real_t p_val);
- _FORCE_INLINE_ real_t get_stiffness() const { return stiffness; }
+ void set_total_mass(real_t p_val);
+ _FORCE_INLINE_ real_t get_total_mass() const { return total_mass; }
+
+ void set_linear_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_linear_stiffness() const { return linear_stiffness; }
+
+ void set_areaAngular_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_areaAngular_stiffness() const { return areaAngular_stiffness; }
+
+ void set_volume_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_volume_stiffness() const { return volume_stiffness; }
+
void set_simulation_precision(int p_val);
_FORCE_INLINE_ int get_simulation_precision() const { return simulation_precision; }
+
void set_pressure_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_pressure_coefficient() const { return pressure_coefficient; }
+
+ void set_pose_matching_coefficient(real_t p_val);
+ _FORCE_INLINE_ real_t get_pose_matching_coefficient() const { return pose_matching_coefficient; }
+
void set_damping_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_damping_coefficient() const { return damping_coefficient; }
+
void set_drag_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; }
private:
- void reload_soft_body();
- void create_soft_body();
- void destroy_soft_body();
+ void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices);
+ void setup_soft_body();
+
+ void pin_node(int p_node_index);
+ void unpin_node(int p_node_index);
+ int search_node_pinned(int p_node_index) const;
};
#endif // SOFT_BODY_BULLET_H
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 331c4a5eba..132c3739d6 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -36,6 +36,7 @@
#include "constraint_bullet.h"
#include "godot_collision_configuration.h"
#include "godot_collision_dispatcher.h"
+#include "project_settings.h"
#include "rigid_body_bullet.h"
#include "servers/physics_server.h"
#include "soft_body_bullet.h"
@@ -325,7 +326,7 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_
}
}
-SpaceBullet::SpaceBullet(bool p_create_soft_world) :
+SpaceBullet::SpaceBullet() :
broadphase(NULL),
dispatcher(NULL),
solver(NULL),
@@ -338,7 +339,7 @@ SpaceBullet::SpaceBullet(bool p_create_soft_world) :
gravityMagnitude(10),
contactDebugCount(0) {
- create_empty_world(p_create_soft_world);
+ create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true));
direct_access = memnew(BulletPhysicsDirectSpaceState(this));
}
@@ -355,6 +356,7 @@ void SpaceBullet::flush_queries() {
}
void SpaceBullet::step(real_t p_delta_time) {
+ delta_time = p_delta_time;
dynamicsWorld->stepSimulation(p_delta_time, 0, 0);
}
@@ -483,6 +485,7 @@ void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) {
void SpaceBullet::add_soft_body(SoftBodyBullet *p_body) {
if (is_using_soft_world()) {
if (p_body->get_bt_soft_body()) {
+ p_body->get_bt_soft_body()->m_worldInfo = get_soft_body_world_info();
static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->addSoftBody(p_body->get_bt_soft_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
}
} else {
@@ -494,6 +497,7 @@ void SpaceBullet::remove_soft_body(SoftBodyBullet *p_body) {
if (is_using_soft_world()) {
if (p_body->get_bt_soft_body()) {
static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->removeSoftBody(p_body->get_bt_soft_body());
+ p_body->get_bt_soft_body()->m_worldInfo = NULL;
}
}
}
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index a6c2786878..006c6462cf 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -84,7 +84,7 @@ public:
};
class SpaceBullet : public RIDBullet {
-private:
+
friend class AreaBullet;
friend void onBulletTickCallback(btDynamicsWorld *world, btScalar timeStep);
friend class BulletPhysicsDirectSpaceState;
@@ -109,12 +109,14 @@ private:
Vector<Vector3> contactDebug;
int contactDebugCount;
+ real_t delta_time;
public:
- SpaceBullet(bool p_create_soft_world);
+ SpaceBullet();
virtual ~SpaceBullet();
void flush_queries();
+ real_t get_delta_time() { return delta_time; }
void step(real_t p_delta_time);
_FORCE_INLINE_ btBroadphaseInterface *get_broadphase() { return broadphase; }
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 4012e821bb..70ca8d68b8 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -110,7 +110,6 @@ struct ClassAPI {
bool is_singleton;
bool is_instanciable;
// @Unclear
- bool is_creatable;
bool is_reference;
List<MethodAPI> methods;
@@ -385,7 +384,6 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back(String("\t\t\"instanciable\": ") + (api.is_instanciable ? "true" : "false") + ",\n");
source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n");
// @Unclear
- // source.push_back(String("\t\t\"createable\": ") + (api.is_creatable ? "true" : "false") + ",\n");
source.push_back("\t\t\"constants\": {\n");
for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) {
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 6efe9b283d..996e73a4bb 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1609,7 +1609,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
- MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate);
+ MonoType *raw_type = p_delegate->get_mono_type();
if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
// Arguments are accessibles as arguments of .Invoke method
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 6fa317ee70..307a7d3e94 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -100,8 +100,6 @@
#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
-#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
#define BINDINGS_GENERATOR_VERSION UINT32_C(2)
@@ -1338,7 +1336,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else if (return_type->cs_out.empty()) {
p_output.push_back("return " + im_call + ";\n");
} else {
- p_output.push_back(INDENT3);
p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
p_output.push_back("\n");
}
@@ -2344,7 +2341,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
- INSERT_ARRAY(Array, object);
INSERT_ARRAY(PoolIntArray, int);
INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte);
@@ -2362,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#undef INSERT_ARRAY
+ // Array
+ itype = TypeInterface();
+ itype.name = "Array";
+ itype.cname = itype.name;
+ itype.proxy_name = "Array";
+ itype.c_out = "\treturn memnew(Array(%1));\n";
+ itype.c_type = itype.name;
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
+ itype.cs_type = itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new Array(%0);";
+ itype.im_type_in = "IntPtr";
+ itype.im_type_out = "IntPtr";
+ builtin_types.insert(itype.cname, itype);
+
// Dictionary
itype = TypeInterface();
itype.name = "Dictionary";
itype.cname = itype.name;
- itype.proxy_name = "Dictionary<object, object>";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n";
- itype.c_arg_in = "&%s_in";
+ itype.proxy_name = "Dictionary";
+ itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
- itype.c_type_in = "MonoObject*";
- itype.c_type_out = "MonoObject*";
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new Dictionary(%0);";
+ itype.im_type_in = "IntPtr";
+ itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
new file mode 100644
index 0000000000..0551c1991a
--- /dev/null
+++ b/modules/mono/glue/collections_glue.cpp
@@ -0,0 +1,240 @@
+/*************************************************************************/
+/* collections_glue.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "collections_glue.h"
+
+#include <mono/metadata/exception.h>
+
+#include "../mono_gd/gd_mono_class.h"
+
+Array *godot_icall_Array_Ctor() {
+ return memnew(Array);
+}
+
+void godot_icall_Array_Dtor(Array *ptr) {
+ memdelete(ptr);
+}
+
+MonoObject *godot_icall_Array_At(Array *ptr, int index) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return NULL;
+ }
+ return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
+}
+
+void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+int godot_icall_Array_Count(Array *ptr) {
+ return ptr->size();
+}
+
+void godot_icall_Array_Add(Array *ptr, MonoObject *item) {
+ ptr->append(GDMonoMarshal::mono_object_to_variant(item));
+}
+
+void godot_icall_Array_Clear(Array *ptr) {
+ ptr->clear();
+}
+
+bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
+ return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
+}
+
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
+ int count = ptr->size();
+
+ if (mono_array_length(array) < (array_index + count)) {
+ MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ GDMonoUtils::set_pending_exception(exc);
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
+ mono_array_setref(array, array_index, boxed);
+ array_index++;
+ }
+}
+
+int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
+ return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
+}
+
+void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
+}
+
+bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
+ int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
+ if (idx >= 0) {
+ ptr->remove(idx);
+ return true;
+ }
+ return false;
+}
+
+void godot_icall_Array_RemoveAt(Array *ptr, int index) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->remove(index);
+}
+
+Dictionary *godot_icall_Dictionary_Ctor() {
+ return memnew(Dictionary);
+}
+
+void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
+ memdelete(ptr);
+}
+
+MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ if (ret == NULL) {
+ MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!exc);
+#endif
+ GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::set_pending_exception((MonoException *)exc);
+ return NULL;
+ }
+ return GDMonoMarshal::variant_to_mono_object(ret);
+}
+
+void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+Array *godot_icall_Dictionary_Keys(Dictionary *ptr) {
+ return memnew(Array(ptr->keys()));
+}
+
+Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
+ return memnew(Array(ptr->values()));
+}
+
+int godot_icall_Dictionary_Count(Dictionary *ptr) {
+ return ptr->size();
+}
+
+void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
+ Variant *ret = ptr->getptr(varKey);
+ if (ret != NULL) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
+ return;
+ }
+ ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+void godot_icall_Dictionary_Clear(Dictionary *ptr) {
+ ptr->clear();
+}
+
+bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ // no dupes
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value);
+}
+
+bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
+ return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
+}
+
+bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
+ return ptr->erase_checked(GDMonoMarshal::mono_object_to_variant(key));
+}
+
+bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
+
+ // no dupes
+ Variant *ret = ptr->getptr(varKey);
+ if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
+ ptr->erase_checked(varKey);
+ return true;
+ }
+
+ return false;
+}
+
+bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ if (ret == NULL) {
+ *value = NULL;
+ return false;
+ }
+ *value = GDMonoMarshal::variant_to_mono_object(ret);
+ return true;
+}
+
+void godot_register_collections_icalls() {
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
+
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
+}
diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h
new file mode 100644
index 0000000000..eb5ecfb725
--- /dev/null
+++ b/modules/mono/glue/collections_glue.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* collections_glue.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef COLLECTIONS_GLUE_H
+#define COLLECTIONS_GLUE_H
+
+#include "core/array.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+// Array
+
+Array *godot_icall_Array_Ctor();
+
+void godot_icall_Array_Dtor(Array *ptr);
+
+MonoObject *godot_icall_Array_At(Array *ptr, int index);
+
+void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value);
+
+int godot_icall_Array_Count(Array *ptr);
+
+void godot_icall_Array_Add(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_Clear(Array *ptr);
+
+bool godot_icall_Array_Contains(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
+
+int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
+
+bool godot_icall_Array_Remove(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_RemoveAt(Array *ptr, int index);
+
+// Dictionary
+
+Dictionary *godot_icall_Dictionary_Ctor();
+
+void godot_icall_Dictionary_Dtor(Dictionary *ptr);
+
+MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key);
+
+void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+Array *godot_icall_Dictionary_Keys(Dictionary *ptr);
+
+Array *godot_icall_Dictionary_Values(Dictionary *ptr);
+
+int godot_icall_Dictionary_Count(Dictionary *ptr);
+
+void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+void godot_icall_Dictionary_Clear(Dictionary *ptr);
+
+bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
+
+bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
+
+bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
+
+// Register internal calls
+
+void godot_register_collections_icalls();
+
+#endif // COLLECTIONS_GLUE_H
diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/cs_files/Array.cs
new file mode 100644
index 0000000000..51f57daef4
--- /dev/null
+++ b/modules/mono/glue/cs_files/Array.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ class ArraySafeHandle : SafeHandle
+ {
+ public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Array.godot_icall_Array_Dtor(handle);
+ return true;
+ }
+ }
+
+ public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Array_Count(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Add(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Clear(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
+
+ ArraySafeHandle safeHandle;
+ bool disposed = false;
+
+ public Array()
+ {
+ safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ }
+
+ internal Array(ArraySafeHandle handle)
+ {
+ safeHandle = handle;
+ }
+
+ internal Array(IntPtr handle)
+ {
+ safeHandle = new ArraySafeHandle(handle);
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return safeHandle.DangerousGetHandle();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (safeHandle != null)
+ {
+ safeHandle.Dispose();
+ safeHandle = null;
+ }
+
+ disposed = true;
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ return godot_icall_Array_At(GetPtr(), index);
+ }
+ set
+ {
+ godot_icall_Array_SetAt(GetPtr(), index, value);
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return godot_icall_Array_Count(GetPtr());
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(object item)
+ {
+ godot_icall_Array_Add(GetPtr(), item);
+ }
+
+ public void Clear()
+ {
+ godot_icall_Array_Clear(GetPtr());
+ }
+
+ public bool Contains(object item)
+ {
+ return godot_icall_Array_Contains(GetPtr(), item);
+ }
+
+ public void CopyTo(object[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
+ // Internal call may throw ArgumentException
+ godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex);
+ }
+
+ public IEnumerator<object> GetEnumerator()
+ {
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ yield return godot_icall_Array_At(GetPtr(), i);
+ }
+ }
+
+ public int IndexOf(object item)
+ {
+ return godot_icall_Array_IndexOf(GetPtr(), item);
+ }
+
+ public void Insert(int index, object item)
+ {
+ godot_icall_Array_Insert(GetPtr(), index, item);
+ }
+
+ public bool Remove(object item)
+ {
+ return godot_icall_Array_Remove(GetPtr(), item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ godot_icall_Array_RemoveAt(GetPtr(), index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+ public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
+ {
+ Array objectArray;
+
+ public Array()
+ {
+ objectArray = new Array();
+ }
+
+ public Array(Array array)
+ {
+ objectArray = array;
+ }
+
+ internal Array(IntPtr handle)
+ {
+ objectArray = new Array(handle);
+ }
+
+ internal Array(ArraySafeHandle handle)
+ {
+ objectArray = new Array(handle);
+ }
+
+ public static explicit operator Array(Array<T> from)
+ {
+ return from.objectArray;
+ }
+
+ public T this[int index]
+ {
+ get
+ {
+ return (T)objectArray[index];
+ }
+ set
+ {
+ objectArray[index] = value;
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return objectArray.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return objectArray.IsReadOnly;
+ }
+ }
+
+ public void Add(T item)
+ {
+ objectArray.Add(item);
+ }
+
+ public void Clear()
+ {
+ objectArray.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return objectArray.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
+ // TODO This may be quite slow because every element access is an internal call.
+ // It could be moved entirely to an internal call if we find out how to do the cast there.
+
+ int count = objectArray.Count;
+
+ if (array.Length < (arrayIndex + count))
+ throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+
+ for (int i = 0; i < count; i++)
+ {
+ array[arrayIndex] = (T)objectArray[i];
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ int count = objectArray.Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ yield return (T)objectArray[i];
+ }
+ }
+
+ public int IndexOf(T item)
+ {
+ return objectArray.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ objectArray.Insert(index, item);
+ }
+
+ public bool Remove(T item)
+ {
+ return objectArray.Remove(item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ objectArray.RemoveAt(index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/cs_files/Dictionary.cs
new file mode 100644
index 0000000000..57a1960ad9
--- /dev/null
+++ b/modules/mono/glue/cs_files/Dictionary.cs
@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ class DictionarySafeHandle : SafeHandle
+ {
+ public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Dictionary.godot_icall_Dictionary_Dtor(handle);
+ return true;
+ }
+ }
+
+ public class Dictionary :
+ IDictionary<object, object>,
+ ICollection<KeyValuePair<object, object>>,
+ IEnumerable<KeyValuePair<object, object>>,
+ IDisposable
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
+
+ DictionarySafeHandle safeHandle;
+ bool disposed = false;
+
+ public Dictionary()
+ {
+ safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ }
+
+ internal Dictionary(DictionarySafeHandle handle)
+ {
+ safeHandle = handle;
+ }
+
+ internal Dictionary(IntPtr handle)
+ {
+ safeHandle = new DictionarySafeHandle(handle);
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return safeHandle.DangerousGetHandle();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (safeHandle != null)
+ {
+ safeHandle.Dispose();
+ safeHandle = null;
+ }
+
+ disposed = true;
+ }
+
+ public object this[object key]
+ {
+ get
+ {
+ return godot_icall_Dictionary_GetValue(GetPtr(), key);
+ }
+ set
+ {
+ godot_icall_Dictionary_SetValue(GetPtr(), key, value);
+ }
+ }
+
+ public ICollection<object> Keys
+ {
+ get
+ {
+ IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
+ return new Array(new ArraySafeHandle(handle));
+ }
+ }
+
+ public ICollection<object> Values
+ {
+ get
+ {
+ IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
+ return new Array(new ArraySafeHandle(handle));
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return godot_icall_Dictionary_Count(GetPtr());
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(object key, object value)
+ {
+ godot_icall_Dictionary_Add(GetPtr(), key, value);
+ }
+
+ public void Add(KeyValuePair<object, object> item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ godot_icall_Dictionary_Clear(GetPtr());
+ }
+
+ public bool Contains(KeyValuePair<object, object> item)
+ {
+ return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value);
+ }
+
+ public bool ContainsKey(object key)
+ {
+ return godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ }
+
+ public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex)
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array keys = (Array)Keys;
+ Array values = (Array)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]);
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array keys = (Array)Keys;
+ Array values = (Array)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ yield return new KeyValuePair<object, object>(keys[i], values[i]);
+ }
+ }
+
+ public bool Remove(object key)
+ {
+ return godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ }
+
+ public bool Remove(KeyValuePair<object, object> item)
+ {
+ return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
+ }
+
+ public bool TryGetValue(object key, out object value)
+ {
+ object retValue;
+ bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue);
+ value = found ? retValue : default(object);
+ return found;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+
+ public class Dictionary<TKey, TValue> :
+ IDictionary<TKey, TValue>,
+ ICollection<KeyValuePair<TKey, TValue>>,
+ IEnumerable<KeyValuePair<TKey, TValue>>
+ {
+ Dictionary objectDict;
+
+ public Dictionary()
+ {
+ objectDict = new Dictionary();
+ }
+
+ public Dictionary(Dictionary dictionary)
+ {
+ objectDict = dictionary;
+ }
+
+ internal Dictionary(IntPtr handle)
+ {
+ objectDict = new Dictionary(handle);
+ }
+
+ internal Dictionary(DictionarySafeHandle handle)
+ {
+ objectDict = new Dictionary(handle);
+ }
+
+ public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
+ {
+ return from.objectDict;
+ }
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return (TValue)objectDict[key];
+ }
+ set
+ {
+ objectDict[key] = value;
+ }
+ }
+
+ public ICollection<TKey> Keys
+ {
+ get
+ {
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr());
+ return new Array<TKey>(new ArraySafeHandle(handle));
+ }
+ }
+
+ public ICollection<TValue> Values
+ {
+ get
+ {
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr());
+ return new Array<TValue>(new ArraySafeHandle(handle));
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return objectDict.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return objectDict.IsReadOnly;
+ }
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ objectDict.Add(key, value);
+ }
+
+ public void Add(KeyValuePair<TKey, TValue> item)
+ {
+ objectDict.Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ objectDict.Clear();
+ }
+
+ public bool Contains(KeyValuePair<TKey, TValue> item)
+ {
+ return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return objectDict.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array<TKey> keys = (Array<TKey>)Keys;
+ Array<TValue> values = (Array<TValue>)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array<TKey> keys = (Array<TKey>)Keys;
+ Array<TValue> values = (Array<TValue>)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ }
+ }
+
+ public bool Remove(TKey key)
+ {
+ return objectDict.Remove(key);
+ }
+
+ public bool Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value));
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ object retValue;
+ bool found = objectDict.TryGetValue(key, out retValue);
+ value = found ? (TValue)retValue : default(TValue);
+ return found;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index ff4477cc6c..6ad4b3dcb2 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -1,36 +1,17 @@
using System;
-using System.Collections.Generic;
namespace Godot
{
- internal static class MarshalUtils
+ static class MarshalUtils
{
- private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
+ static bool IsArrayGenericType(Type type)
{
- var ret = new Dictionary<object, object>();
-
- for (int i = 0; i < keys.Length; i++)
- {
- ret.Add(keys[i], values[i]);
- }
-
- return ret;
- }
-
- private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
- {
- var keys = from.Keys;
- keysTo = new object[keys.Count];
- keys.CopyTo(keysTo, 0);
-
- var values = from.Values;
- valuesTo = new object[values.Count];
- values.CopyTo(valuesTo, 0);
+ return type.GetGenericTypeDefinition() == typeof(Array<>);
}
- private static Type GetDictionaryType()
+ static bool IsDictionaryGenericType(Type type)
{
- return typeof(Dictionary<object, object>);
+ return type.GetGenericTypeDefinition() == typeof(Dictionary<, >);
}
}
}
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index cedc8e9992..6a6f3062b4 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "builtin_types_glue.h"
+#include "collections_glue.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_class.h"
@@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) {
void godot_register_header_icalls() {
godot_register_builtin_type_icalls();
+ godot_register_collections_icalls();
}
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 66339d7ae6..e2597a7d42 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -33,23 +33,37 @@
#include <mono/metadata/attrdefs.h>
#include "gd_mono_assembly.h"
+#include "gd_mono_marshal.h"
-MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) {
+String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
+ // mono_type_get_full_name is not exposed to embedders, but this seems to do the job
+ MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
- return mono_class_get_type(p_class->get_mono_ptr());
-}
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoString *str = mono_object_to_string((MonoObject *)type_obj, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
-bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+ return GDMonoMarshal::mono_string_to_godot(str);
+}
- return mono_class_is_assignable_from(mono_class, p_from->mono_class);
+MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
+ return mono_class_get_type(p_mono_class);
}
String GDMonoClass::get_full_name() const {
+ return get_full_name(mono_class);
+}
- String res = namespace_name;
- if (res.length())
- res += ".";
- return res + class_name;
+MonoType *GDMonoClass::get_mono_type() {
+ // Care, you cannot compare MonoType pointers
+ return get_mono_type(mono_class);
+}
+
+bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+
+ return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
GDMonoClass *GDMonoClass::get_parent_class() {
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 417c138594..f81ab84cd0 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -98,7 +98,11 @@ class GDMonoClass {
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
public:
- static MonoType *get_raw_type(GDMonoClass *p_class);
+ static String get_full_name(MonoClass *p_mono_class);
+ static MonoType *get_mono_type(MonoClass *p_mono_class);
+
+ String get_full_name() const;
+ MonoType *get_mono_type();
bool is_assignable_from(GDMonoClass *p_from) const;
@@ -108,8 +112,6 @@ public:
_FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
- String get_full_name() const;
-
GDMonoClass *get_parent_class();
#ifdef TOOLS_ENABLED
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 3b91777ed4..d3a673dc1b 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
SET_FROM_ARRAY_AND_BREAK(Array);
@@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL();
} break;
@@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
case Variant::DICTIONARY: {
- MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ } break;
+ case Variant::ARRAY: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
} break;
- case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
@@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
- MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ exc = NULL;
+
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
mono_field_set_value(p_object, mono_field, managed);
break;
}
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index 2b5110f0b9..72a5439044 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -45,7 +45,8 @@ struct ManagedType {
GDMonoClass *type_class;
ManagedType() {
- type_class = 0;
+ type_encoding = 0;
+ type_class = NULL;
}
};
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 5cd77d63e2..de91e71bab 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return Variant::ARRAY;
@@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (CACHED_CLASS(RID) == type_class) {
return Variant::_RID;
}
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ return Variant::DICTIONARY;
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ return Variant::ARRAY;
+ }
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
return Variant::DICTIONARY;
}
+
+ exc = NULL;
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ return Variant::ARRAY;
+ }
} break;
default: {
@@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type;
type.type_encoding = MONO_TYPE_OBJECT;
+ // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding
return variant_to_mono_object(p_var, type);
}
@@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
@@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (CACHED_CLASS(RID) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator RID());
}
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+ }
} break;
case MONO_TYPE_OBJECT: {
// Variant
@@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
}
case Variant::DICTIONARY:
- return Dictionary_to_mono_object(p_var->operator Dictionary());
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
case Variant::ARRAY:
- return (MonoObject *)Array_to_mono_array(p_var->operator Array());
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
case Variant::POOL_BYTE_ARRAY:
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
case Variant::POOL_INT_ARRAY:
@@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
- return Dictionary_to_mono_object(p_var->operator Dictionary());
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
+ }
+
+ exc = NULL;
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
}
} break;
} break;
@@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
ERR_FAIL_COND_V(!tclass, Variant());
- MonoType *raw_type = tclass->get_raw_type(tclass);
+ MonoType *raw_type = tclass->get_mono_type();
ManagedType type;
@@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return mono_array_to_Array((MonoArray *)p_obj);
@@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
return ptr ? Variant(*ptr) : Variant();
}
+
+ if (CACHED_CLASS(Array) == type_class) {
+ MonoException *exc = NULL;
+ GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr);
+ Array *ptr = get_ptr(p_obj, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return ptr ? Variant(*ptr) : Variant();
+ }
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ MonoException *exc = NULL;
+ GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr);
+ Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return ptr ? Variant(*ptr) : Variant();
+ }
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
- return mono_object_to_Dictionary(p_obj);
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ MonoException *exc = NULL;
+ MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return *unbox<Dictionary *>(ret);
+ }
+
+ exc = NULL;
+
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ MonoException *exc = NULL;
+ MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return *unbox<Array *>(ret);
}
} break;
}
@@ -822,66 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret;
}
-
-MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
- MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
- MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
-
- int i = 0;
- const Variant *dkey = NULL;
- while ((dkey = p_dict.next(dkey))) {
- mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey));
- mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey]));
- i++;
- }
-
- GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
-
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL_V(NULL);
- }
-
- return ret;
-}
-
-Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
- Dictionary ret;
-
- if (!p_dict)
- return ret;
-
- GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays);
-
- MonoArray *keys = NULL;
- MonoArray *values = NULL;
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL_V(Dictionary());
- }
-
- int length = mono_array_length(keys);
-
- for (int i = 0; i < length; i++) {
- MonoObject *key_obj = mono_array_get(keys, MonoObject *, i);
- MonoObject *value_obj = mono_array_get(values, MonoObject *, i);
-
- Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant();
- Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant();
-
- ret[key] = value;
- }
-
- return ret;
-}
} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 6572408ab5..464f584a0a 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
-// Dictionary
-
-MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict);
-Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
-
#ifdef YOLO_COPY
#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 1f837a2d78..a1c710c26c 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -139,23 +139,8 @@ bool GDMonoProperty::has_setter() {
}
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
- MonoMethod *prop_method = mono_property_get_set_method(mono_property);
-
- MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
- mono_array_set(params, MonoObject *, 0, p_value);
-
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
-
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
+ void *params[1] = { p_value };
+ set_value(p_object, params, r_exc);
}
void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index a229552b76..7cd922138f 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -87,6 +87,8 @@ void MonoCache::clear_members() {
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif
+ class_KeyNotFoundException = NULL;
+
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
@@ -107,6 +109,8 @@ void MonoCache::clear_members() {
class_Control = NULL;
class_Spatial = NULL;
class_WeakRef = NULL;
+ class_Array = NULL;
+ class_Dictionary = NULL;
class_MarshalUtils = NULL;
#ifdef DEBUG_ENABLED
@@ -134,8 +138,10 @@ void MonoCache::clear_members() {
field_Image_ptr = NULL;
field_RID_ptr = NULL;
- methodthunk_MarshalUtils_DictionaryToArrays = NULL;
- methodthunk_MarshalUtils_ArraysToDictionary = NULL;
+ methodthunk_Array_GetPtr = NULL;
+ methodthunk_Dictionary_GetPtr = NULL;
+ methodthunk_MarshalUtils_IsArrayGenericType = NULL;
+ methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
@@ -175,6 +181,8 @@ void update_corlib_cache() {
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
+ CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
+
mono_cache.corlib_cache_updated = true;
}
@@ -198,6 +206,8 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
+ CACHE_CLASS_AND_CHECK(Array, GODOT_API_CLASS(Array));
+ CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
#ifdef DEBUG_ENABLED
@@ -224,8 +234,10 @@ void update_godot_api_cache() {
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method("GetPtr", 0)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(Dictionary)->get_method("GetPtr", 0)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsArrayGenericType", 1)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsDictionaryGenericType", 1)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
@@ -234,24 +246,9 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
#endif
- {
- /*
- * TODO Right now we only support Dictionary<object, object>.
- * It would be great if we could support other key/value types
- * without forcing the user to copy the entries.
- */
- GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0);
- ERR_FAIL_NULL(method_get_dict_type);
- MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL);
- ERR_FAIL_NULL(dict_refl_type);
- MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type);
- ERR_FAIL_NULL(dict_type);
-
- CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type));
- }
-
+ // TODO Move to CSharpLanguage::init()
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
- mono_runtime_object_init(task_scheduler);
+ GDMonoUtils::runtime_object_init(task_scheduler);
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true;
@@ -304,6 +301,12 @@ MonoThread *get_current_thread() {
return mono_thread_current();
}
+void runtime_object_init(MonoObject *p_this_obj) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_object_init(p_this_obj);
+ GD_MONO_END_RUNTIME_INVOKE;
+}
+
GDMonoClass *get_object_class(MonoObject *p_object) {
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
}
@@ -358,7 +361,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
return mono_object;
}
@@ -368,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
@@ -380,13 +383,73 @@ MonoObject *create_managed_from(const RID &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
return mono_object;
}
+MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Search constructor that takes a pointer as parameter
+ MonoMethod *m;
+ void *iter = NULL;
+ while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
+ if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
+ MonoMethodSignature *sig = mono_method_signature(m);
+ void *front = NULL;
+ if (mono_signature_get_param_count(sig) == 1 &&
+ mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
+ break;
+ }
+ }
+ }
+
+ CRASH_COND(m == NULL);
+
+ Array *new_array = memnew(Array(p_from));
+ void *args[1] = { &new_array };
+
+ MonoException *exc = NULL;
+ mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ return mono_object;
+}
+
+MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Search constructor that takes a pointer as parameter
+ MonoMethod *m;
+ void *iter = NULL;
+ while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
+ if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
+ MonoMethodSignature *sig = mono_method_signature(m);
+ void *front = NULL;
+ if (mono_signature_get_param_count(sig) == 1 &&
+ mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
+ break;
+ }
+ }
+ }
+
+ CRASH_COND(m == NULL);
+
+ Dictionary *new_dict = memnew(Dictionary(p_from));
+ void *args[1] = { &new_dict };
+
+ MonoException *exc = NULL;
+ mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ return mono_object;
+}
+
MonoDomain *create_domain(const String &p_friendly_name) {
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
@@ -400,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain;
}
-String get_exception_name_and_message(MonoException *p_ex) {
+String get_exception_name_and_message(MonoException *p_exc) {
String res;
- MonoClass *klass = mono_object_get_class((MonoObject *)p_ex);
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type);
@@ -413,12 +476,24 @@ String get_exception_name_and_message(MonoException *p_ex) {
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL);
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_exc, NULL, NULL);
+ GD_MONO_END_RUNTIME_INVOKE;
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
}
+void set_exception_message(MonoException *p_exc, String message) {
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
+ MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
+ MonoString *msg = GDMonoMarshal::mono_string_from_godot(message);
+ void *params[1] = { msg };
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_property_set_value(prop, (MonoObject *)p_exc, params, NULL);
+ GD_MONO_END_RUNTIME_INVOKE;
+}
+
void debug_print_unhandled_exception(MonoException *p_exc) {
print_unhandled_exception(p_exc);
debug_send_unhandled_exception_error(p_exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 4f8e5932cd..d6774ed41d 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -41,14 +41,24 @@
#include "object.h"
#include "reference.h"
+#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \
+ if (unlikely(m_exc != NULL)) { \
+ GDMonoUtils::debug_unhandled_exception(m_exc); \
+ _UNREACHABLE_(); \
+ }
+
namespace GDMonoUtils {
-typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
-typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
+typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **);
+typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
struct MonoCache {
@@ -79,6 +89,8 @@ struct MonoCache {
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
+ GDMonoClass *class_KeyNotFoundException;
+
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
@@ -100,6 +112,8 @@ struct MonoCache {
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef;
+ GDMonoClass *class_Array;
+ GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils;
#ifdef DEBUG_ENABLED
@@ -127,8 +141,10 @@ struct MonoCache {
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
- MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
- MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
+ Array_GetPtr methodthunk_Array_GetPtr;
+ Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
+ IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
+ IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
@@ -175,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() {
return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
}
+void runtime_object_init(MonoObject *p_this_obj);
+
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
@@ -183,10 +201,13 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
MonoObject *create_managed_from(const NodePath &p_from);
MonoObject *create_managed_from(const RID &p_from);
+MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
+MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class);
MonoDomain *create_domain(const String &p_friendly_name);
-String get_exception_name_and_message(MonoException *p_ex);
+String get_exception_name_and_message(MonoException *p_exc);
+void set_exception_message(MonoException *p_exc, String message);
void debug_print_unhandled_exception(MonoException *p_exc);
void debug_send_unhandled_exception_error(MonoException *p_exc);
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 1405fa98b0..00c36ebb47 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -64,7 +64,6 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
String str = "ws://";
String proto_string = "";
- int i = 0;
if (p_ssl)
str = "wss://";
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index fd834193dd..28e3ea962f 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -146,9 +146,6 @@ void AudioDriverOpenSL::start() {
res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void *)&EngineItf);
ERR_FAIL_COND(res != SL_RESULT_SUCCESS);
- /* Initialize arrays required[] and iidArray[] */
- SLboolean required[MAX_NUMBER_INTERFACES];
- SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
{
const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB };
@@ -188,10 +185,7 @@ void AudioDriverOpenSL::start() {
//cntxt.pDataBase = (void*)&pcmData;
//cntxt.pData = cntxt.pDataBase;
//cntxt.size = sizeof(pcmData);
- /* Set arrays required[] and iidArray[] for SEEK interface
- (PlayItf is implicit) */
- required[0] = SL_BOOLEAN_TRUE;
- iidArray[0] = SL_IID_BUFFERQUEUE;
+
/* Create the music player */
{
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index 3e40b59de9..ee5ae156b7 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -153,7 +153,6 @@ String DirAccessJAndroid::get_current_dir() {
bool DirAccessJAndroid::file_exists(String p_file) {
- JNIEnv *env = ThreadAndroid::get_env();
String sd;
if (current_dir == "")
sd = p_file;
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index c562a47b00..59e35884d1 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -528,11 +528,9 @@ class EditorExportAndroid : public EditorExportPlatform {
bool exported = false;
for (int i = 0; i < p_so.tags.size(); ++i) {
// shared objects can be fat (compatible with multiple ABIs)
- int start_pos = 0;
int abi_index = abis.find(p_so.tags[i]);
if (abi_index != -1) {
exported = true;
- start_pos = abi_index + 1;
String abi = abis[abi_index];
String dst_path = "lib/" + abi + "/" + p_so.path.get_file();
Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path);
diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp
index e6240ad9e9..8bb1c38345 100644
--- a/platform/android/java_glue.cpp
+++ b/platform/android/java_glue.cpp
@@ -880,7 +880,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo
const char **cmdline = NULL;
int cmdlen = 0;
- bool use_apk_expansion = false;
if (p_cmdline) {
cmdlen = env->GetArrayLength(p_cmdline);
if (cmdlen) {
@@ -891,9 +890,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo
jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
const char *rawString = env->GetStringUTFChars(string, 0);
- if (rawString && strcmp(rawString, "--main-pack") == 0) {
- use_apk_expansion = true;
- }
cmdline[i] = rawString;
}
diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp
index e85813605f..b13baf69c2 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -132,7 +132,7 @@ JNIEnv *ThreadAndroid::get_env() {
}
JNIEnv *env = NULL;
- int status = java_vm->AttachCurrentThread(&env, NULL);
+ java_vm->AttachCurrentThread(&env, NULL);
return env;
}
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 4c1e02baf7..5480d30e7a 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -781,7 +781,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
int ret = unzGoToFirstFile(src_pkg_zip);
Vector<uint8_t> project_file_data;
while (ret == UNZ_OK) {
+#if defined(OSX_ENABLED) || defined(X11_ENABLED)
bool is_execute = false;
+#endif
//get filename
unz_file_info info;
@@ -812,7 +814,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
continue; //ignore!
}
found_library = true;
+#if defined(OSX_ENABLED) || defined(X11_ENABLED)
is_execute = true;
+#endif
file = "godot_ios.a";
}
if (file == project_file) {
@@ -855,7 +859,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
f->close();
memdelete(f);
-#ifdef OSX_ENABLED
+#if defined(OSX_ENABLED) || defined(X11_ENABLED)
if (is_execute) {
// we need execute rights on this file
chmod(file.utf8().get_data(), 0755);
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index 74ad3e79d0..a3f1b25e05 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -292,7 +292,6 @@ void LineBuilder::build() {
color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
}
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
- uvx0 = current_distance0 / (width * tile_aspect);
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
uvx0 = current_distance0 / total_distance;
@@ -306,7 +305,6 @@ void LineBuilder::build() {
u0 = u1;
f0 = f1;
pos0 = pos1;
- current_distance0 = current_distance1;
if (intersection_result == SEGMENT_INTERSECT) {
if (current_joint_mode == Line2D::LINE_JOINT_SHARP) {
pos_up0 = pos_up1;
diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h
index f31d65e411..f8ef04b78f 100644
--- a/scene/3d/collision_object.h
+++ b/scene/3d/collision_object.h
@@ -39,6 +39,7 @@ class CollisionObject : public Spatial {
GDCLASS(CollisionObject, Spatial);
bool area;
+
RID rid;
struct ShapeData {
diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h
index c9c91a5824..6ca8e80ea1 100644
--- a/scene/3d/collision_shape.h
+++ b/scene/3d/collision_shape.h
@@ -49,6 +49,7 @@ class CollisionShape : public Spatial {
void resource_changed(RES res);
bool disabled;
+protected:
void _create_debug_shape();
void _update_in_shape_owner(bool p_xform_only = false);
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index e836a6154a..722eadb9ca 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -36,6 +36,7 @@
#include "scene/resources/material.h"
#include "scene/scene_string_names.h"
#include "skeleton.h"
+
bool MeshInstance::_set(const StringName &p_name, const Variant &p_value) {
//this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else.
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 5d359cd4d5..0dfec538f9 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -41,6 +41,7 @@ class MeshInstance : public GeometryInstance {
GDCLASS(MeshInstance, GeometryInstance);
+protected:
Ref<Mesh> mesh;
NodePath skeleton_path;
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 8c5d4da117..7529a0e524 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -122,23 +122,23 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const {
void PhysicsBody::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
- PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
}
- ERR_FAIL_COND(!physics_body);
- PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid());
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid());
}
void PhysicsBody::remove_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
- PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
}
- ERR_FAIL_COND(!physics_body);
- PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid());
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
void PhysicsBody::_set_layers(uint32_t p_mask) {
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
new file mode 100644
index 0000000000..8498dc34c0
--- /dev/null
+++ b/scene/3d/soft_body.cpp
@@ -0,0 +1,740 @@
+/*************************************************************************/
+/* soft_physics_body.cpp */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "soft_body.h"
+#include "os/os.h"
+#include "scene/3d/collision_object.h"
+#include "scene/3d/skeleton.h"
+#include "servers/physics_server.h"
+
+SoftBodyVisualServerHandler::SoftBodyVisualServerHandler() {}
+
+void SoftBodyVisualServerHandler::prepare(RID p_mesh, int p_surface) {
+ clear();
+
+ ERR_FAIL_COND(!p_mesh.is_valid());
+
+ mesh = p_mesh;
+ surface = p_surface;
+
+ const uint32_t surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, surface);
+ const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface);
+ const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface);
+ uint32_t surface_offsets[VS::ARRAY_MAX];
+
+ buffer = VS::get_singleton()->mesh_surface_get_array(mesh, surface);
+ stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets);
+ offset_vertices = surface_offsets[VS::ARRAY_VERTEX];
+ offset_normal = surface_offsets[VS::ARRAY_NORMAL];
+}
+
+void SoftBodyVisualServerHandler::clear() {
+
+ if (mesh.is_valid()) {
+ buffer.resize(0);
+ }
+
+ mesh = RID();
+}
+
+void SoftBodyVisualServerHandler::open() {
+ write_buffer = buffer.write();
+}
+
+void SoftBodyVisualServerHandler::close() {
+ write_buffer = PoolVector<uint8_t>::Write();
+}
+
+void SoftBodyVisualServerHandler::commit_changes() {
+ VS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer);
+}
+
+void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) {
+ copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3);
+}
+
+void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) {
+ copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3);
+}
+
+void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) {
+ VS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb);
+}
+
+SoftBody::PinnedPoint::PinnedPoint() :
+ point_index(-1),
+ spatial_attachment(NULL) {
+}
+
+SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
+ point_index = obj_tocopy.point_index;
+ spatial_attachment_path = obj_tocopy.spatial_attachment_path;
+ spatial_attachment = obj_tocopy.spatial_attachment;
+ vertex_offset_transform = obj_tocopy.vertex_offset_transform;
+}
+
+void SoftBody::_update_pickable() {
+ if (!is_inside_tree())
+ return;
+ bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree();
+ PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable);
+}
+
+bool SoftBody::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ String which = name.get_slicec('/', 0);
+
+ if ("pinned_points" == which) {
+
+ return _set_property_pinned_points_indices(p_value);
+
+ } else if ("attachments" == which) {
+
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ return _set_property_pinned_points_attachment(idx, what, p_value);
+ }
+
+ return false;
+}
+
+bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const {
+ String name = p_name;
+ String which = name.get_slicec('/', 0);
+
+ if ("pinned_points" == which) {
+ Array arr_ret;
+ const int pinned_points_indices_size = pinned_points_indices.size();
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ arr_ret.resize(pinned_points_indices_size);
+
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ arr_ret[i] = r[i].point_index;
+ }
+
+ r_ret = arr_ret;
+ return true;
+
+ } else if ("attachments" == which) {
+
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ return _get_property_pinned_points(idx, what, r_ret);
+ }
+
+ return false;
+}
+
+void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ const int pinned_points_indices_size = pinned_points_indices.size();
+
+ p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points"));
+
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index"));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path"));
+ }
+}
+
+bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) {
+
+ const int p_indices_size = p_indices.size();
+
+ { // Remove the pined points on physics server that will be removed by resize
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ if (p_indices_size < pinned_points_indices.size()) {
+ for (int i = pinned_points_indices.size() - 1; i >= p_indices_size; --i) {
+ pin_point(r[i].point_index, false);
+ }
+ }
+ }
+
+ pinned_points_indices.resize(p_indices_size);
+
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ int point_index;
+ for (int i = 0; i < p_indices_size; ++i) {
+ point_index = p_indices.get(i);
+ if (w[i].point_index != point_index) {
+ if (-1 != w[i].point_index)
+ pin_point(w[i].point_index, false);
+ w[i].point_index = point_index;
+ pin_point(w[i].point_index, true);
+ }
+ }
+ return true;
+}
+
+bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) {
+ if (pinned_points_indices.size() <= p_item) {
+ return false;
+ }
+
+ if ("spatial_attachment_path" == p_what) {
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ pin_point(w[p_item].point_index, true, p_value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const {
+ if (pinned_points_indices.size() <= p_item) {
+ return false;
+ }
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+
+ if ("point_index" == p_what) {
+ r_ret = r[p_item].point_index;
+ } else if ("spatial_attachment_path" == p_what) {
+ r_ret = r[p_item].spatial_attachment_path;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) {
+#ifdef TOOLS_ENABLED
+ if (p_changed == this) {
+ update_configuration_warning();
+ }
+#endif
+}
+
+void SoftBody::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_WORLD: {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ add_change_receptor(this);
+
+ RID space = get_world()->get_space();
+ PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space);
+ PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
+ update_physics_server();
+ } break;
+ case NOTIFICATION_READY: {
+ if (!parent_collision_ignore.is_empty())
+ add_collision_exception_with(get_node(parent_collision_ignore));
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (!simulation_started) {
+ PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
+
+ _update_cache_pin_points_datas();
+ // Submit bone attachment
+ const int pinned_points_indices_size = pinned_points_indices.size();
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ for (int i = 0; i < pinned_points_indices_size; ++i) {
+ if (!r[i].spatial_attachment) {
+ // Use soft body position to update the point position
+ PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (get_global_transform() * r[i].vertex_offset_transform).origin);
+ } else {
+ PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (r[i].spatial_attachment->get_global_transform() * r[i].vertex_offset_transform).origin);
+ }
+ }
+ }
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+
+ _update_pickable();
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, RID());
+
+ } break;
+ }
+
+#ifdef TOOLS_ENABLED
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ update_configuration_warning();
+ }
+ }
+
+#endif
+}
+
+void SoftBody::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_draw_soft_mesh"), &SoftBody::_draw_soft_mesh);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody::set_collision_layer);
+ ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody::get_collision_layer);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody::set_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody::get_collision_mask_bit);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody::set_collision_layer_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody::get_collision_layer_bit);
+
+ ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore);
+ ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore);
+
+ ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with);
+ ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with);
+
+ ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody::set_simulation_precision);
+ ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody::get_simulation_precision);
+
+ ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody::set_total_mass);
+ ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody::get_total_mass);
+
+ ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody::set_linear_stiffness);
+ ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody::get_linear_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody::set_areaAngular_stiffness);
+ ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody::get_areaAngular_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody::set_volume_stiffness);
+ ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody::get_volume_stiffness);
+
+ ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody::set_pressure_coefficient);
+ ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody::get_pressure_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody::set_pose_matching_coefficient);
+ ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody::get_pose_matching_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody::set_damping_coefficient);
+ ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody::get_damping_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody::set_drag_coefficient);
+ ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody::get_drag_coefficient);
+
+ ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody::set_ray_pickable);
+ ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody::is_ray_pickable);
+
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient");
+}
+
+String SoftBody::get_configuration_warning() const {
+
+ String warning = MeshInstance::get_configuration_warning();
+
+ if (get_mesh().is_null()) {
+ if (!warning.empty())
+ warning += "\n\n";
+
+ warning += TTR("This body will be ignored until you set a mesh");
+ }
+
+ Transform t = get_transform();
+ if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) {
+ if (!warning.empty())
+ warning += "\n\n";
+
+ warning += TTR("Size changes to SoftBody will be overriden by the physics engine when running.\nChange the size in children collision shapes instead.");
+ }
+
+ return warning;
+}
+
+void SoftBody::_draw_soft_mesh() {
+ if (get_mesh().is_null())
+ return;
+
+ if (!visual_server_handler.is_ready()) {
+
+ visual_server_handler.prepare(get_mesh()->get_rid(), 0);
+
+ /// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
+ simulation_started = true;
+ call_deferred("set_as_toplevel", true);
+ call_deferred("set_transform", Transform());
+ }
+
+ visual_server_handler.open();
+ PhysicsServer::get_singleton()->soft_body_update_visual_server(physics_rid, &visual_server_handler);
+ visual_server_handler.close();
+
+ visual_server_handler.commit_changes();
+}
+
+void SoftBody::update_physics_server() {
+
+ if (Engine::get_singleton()->is_editor_hint())
+ return;
+
+ if (get_mesh().is_valid()) {
+
+ become_mesh_owner();
+ PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
+ VS::get_singleton()->connect("frame_pre_draw", this, "_draw_soft_mesh");
+ } else {
+
+ PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL);
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh");
+ }
+}
+
+void SoftBody::become_mesh_owner() {
+ if (mesh.is_null())
+ return;
+
+ if (!mesh_owner) {
+ mesh_owner = true;
+
+ ERR_FAIL_COND(!mesh->get_surface_count());
+
+ // Get current mesh array and create new mesh array with necessary flag for softbody
+ Array surface_arrays = mesh->surface_get_arrays(0);
+ Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0);
+ uint32_t surface_format = mesh->surface_get_format(0);
+
+ surface_format &= ~(Mesh::ARRAY_COMPRESS_VERTEX | Mesh::ARRAY_COMPRESS_NORMAL);
+ surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
+
+ Ref<ArrayMesh> soft_mesh;
+ soft_mesh.instance();
+ soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format);
+
+ set_mesh(soft_mesh);
+
+ Vector<Ref<Material> > copy_materials;
+ copy_materials.append_array(materials);
+ for (int i = copy_materials.size() - 1; 0 <= i; --i) {
+ set_surface_material(i, copy_materials[i]);
+ }
+ }
+}
+
+void SoftBody::set_collision_mask(uint32_t p_mask) {
+ collision_mask = p_mask;
+ PhysicsServer::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask);
+}
+
+uint32_t SoftBody::get_collision_mask() const {
+ return collision_mask;
+}
+void SoftBody::set_collision_layer(uint32_t p_layer) {
+ collision_layer = p_layer;
+ PhysicsServer::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer);
+}
+
+uint32_t SoftBody::get_collision_layer() const {
+ return collision_layer;
+}
+
+void SoftBody::set_collision_mask_bit(int p_bit, bool p_value) {
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask |= 1 << p_bit;
+ else
+ mask &= ~(1 << p_bit);
+ set_collision_mask(mask);
+}
+
+bool SoftBody::get_collision_mask_bit(int p_bit) const {
+ return get_collision_mask() & (1 << p_bit);
+}
+
+void SoftBody::set_collision_layer_bit(int p_bit, bool p_value) {
+ uint32_t layer = get_collision_layer();
+ if (p_value)
+ layer |= 1 << p_bit;
+ else
+ layer &= ~(1 << p_bit);
+ set_collision_layer(layer);
+}
+
+bool SoftBody::get_collision_layer_bit(int p_bit) const {
+ return get_collision_layer() & (1 << p_bit);
+}
+
+void SoftBody::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) {
+ parent_collision_ignore = p_parent_collision_ignore;
+}
+
+const NodePath &SoftBody::get_parent_collision_ignore() const {
+ return parent_collision_ignore;
+}
+
+void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) {
+ pinned_points_indices = p_pinned_points_indices;
+ PoolVector<PinnedPoint>::Read w = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+ pin_point(p_pinned_points_indices[i].point_index, true);
+ }
+}
+
+PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() {
+ return pinned_points_indices;
+}
+
+void SoftBody::add_collision_exception_with(Node *p_node) {
+ ERR_FAIL_NULL(p_node);
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
+ }
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid());
+}
+
+void SoftBody::remove_collision_exception_with(Node *p_node) {
+ ERR_FAIL_NULL(p_node);
+ CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node);
+ if (!collision_object) {
+ ERR_EXPLAIN("Collision exception only works between two CollisionObject");
+ }
+ ERR_FAIL_COND(!collision_object);
+ PhysicsServer::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid());
+}
+
+int SoftBody::get_simulation_precision() {
+ return PhysicsServer::get_singleton()->soft_body_get_simulation_precision(physics_rid);
+}
+
+void SoftBody::set_simulation_precision(int p_simulation_precision) {
+ PhysicsServer::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision);
+}
+
+real_t SoftBody::get_total_mass() {
+ return PhysicsServer::get_singleton()->soft_body_get_total_mass(physics_rid);
+}
+
+void SoftBody::set_total_mass(real_t p_total_mass) {
+ PhysicsServer::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass);
+}
+
+void SoftBody::set_linear_stiffness(real_t p_linear_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness);
+}
+
+real_t SoftBody::get_linear_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_linear_stiffness(physics_rid);
+}
+
+void SoftBody::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness);
+}
+
+real_t SoftBody::get_areaAngular_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid);
+}
+
+void SoftBody::set_volume_stiffness(real_t p_volume_stiffness) {
+ PhysicsServer::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness);
+}
+
+real_t SoftBody::get_volume_stiffness() {
+ return PhysicsServer::get_singleton()->soft_body_get_volume_stiffness(physics_rid);
+}
+
+real_t SoftBody::get_pressure_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_pressure_coefficient(physics_rid);
+}
+
+void SoftBody::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient);
+}
+
+real_t SoftBody::get_pose_matching_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid);
+}
+
+void SoftBody::set_pressure_coefficient(real_t p_pressure_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient);
+}
+
+real_t SoftBody::get_damping_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_damping_coefficient(physics_rid);
+}
+
+void SoftBody::set_damping_coefficient(real_t p_damping_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient);
+}
+
+real_t SoftBody::get_drag_coefficient() {
+ return PhysicsServer::get_singleton()->soft_body_get_drag_coefficient(physics_rid);
+}
+
+void SoftBody::set_drag_coefficient(real_t p_drag_coefficient) {
+ PhysicsServer::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient);
+}
+
+Vector3 SoftBody::get_point_transform(int p_point_index) {
+ return PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index);
+}
+
+void SoftBody::pin_point_toggle(int p_point_index) {
+ pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index)));
+}
+
+void SoftBody::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) {
+ _pin_point_on_physics_server(p_point_index, pin);
+ if (pin) {
+ _add_pinned_point(p_point_index, p_spatial_attachment_path);
+ } else {
+ _remove_pinned_point(p_point_index);
+ }
+}
+
+bool SoftBody::is_point_pinned(int p_point_index) const {
+ return -1 != _has_pinned_point(p_point_index);
+}
+
+void SoftBody::set_ray_pickable(bool p_ray_pickable) {
+
+ ray_pickable = p_ray_pickable;
+ _update_pickable();
+}
+
+bool SoftBody::is_ray_pickable() const {
+
+ return ray_pickable;
+}
+
+SoftBody::SoftBody() :
+ MeshInstance(),
+ physics_rid(PhysicsServer::get_singleton()->soft_body_create()),
+ mesh_owner(false),
+ collision_mask(1),
+ collision_layer(1),
+ simulation_started(false),
+ pinned_points_cache_dirty(true) {
+
+ PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id());
+}
+
+SoftBody::~SoftBody() {
+}
+
+void SoftBody::reset_softbody_pin() {
+ PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid);
+ PoolVector<PinnedPoint>::Read pps = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 < i; --i) {
+ PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true);
+ }
+}
+
+void SoftBody::_update_cache_pin_points_datas() {
+ if (pinned_points_cache_dirty) {
+ pinned_points_cache_dirty = false;
+
+ PoolVector<PinnedPoint>::Write w = pinned_points_indices.write();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+
+ if (!w[i].spatial_attachment_path.is_empty()) {
+ w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path));
+ if (w[i].spatial_attachment) {
+
+ Transform point_global_transform(get_global_transform());
+ point_global_transform.translate(PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index));
+
+ // Local transform relative to spatial attachment node
+ w[i].vertex_offset_transform = w[i].spatial_attachment->get_global_transform().affine_inverse() * point_global_transform;
+ continue;
+ } else {
+ ERR_PRINTS("The node with path: " + String(w[i].spatial_attachment_path) + " was not found or is not a spatial node.");
+ }
+ }
+ // Local transform relative to Soft body
+ w[i].vertex_offset_transform.origin = PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index);
+ w[i].vertex_offset_transform.basis = Basis();
+ }
+ }
+}
+
+void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) {
+ PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin);
+}
+
+void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) {
+ SoftBody::PinnedPoint *pinned_point;
+ if (-1 == _get_pinned_point(p_point_index, pinned_point)) {
+ // Create new
+ PinnedPoint pp;
+ pp.point_index = p_point_index;
+ pp.spatial_attachment_path = p_spatial_attachment_path;
+ pinned_points_indices.push_back(pp);
+ } else {
+ // Update
+ pinned_point->point_index = p_point_index;
+ pinned_point->spatial_attachment_path = p_spatial_attachment_path;
+ }
+}
+
+void SoftBody::_remove_pinned_point(int p_point_index) {
+ const int id(_has_pinned_point(p_point_index));
+ if (-1 != id) {
+ pinned_points_indices.remove(id);
+ }
+}
+
+int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_point) const {
+ const int id = _has_pinned_point(p_point_index);
+ if (-1 == id) {
+ r_point = NULL;
+ return -1;
+ } else {
+ r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points_indices.read()[id]);
+ return id;
+ }
+}
+
+int SoftBody::_has_pinned_point(int p_point_index) const {
+ PoolVector<PinnedPoint>::Read r = pinned_points_indices.read();
+ for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) {
+ if (p_point_index == r[i].point_index) {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h
new file mode 100644
index 0000000000..f44f337698
--- /dev/null
+++ b/scene/3d/soft_body.h
@@ -0,0 +1,197 @@
+/*************************************************************************/
+/* soft_physics_body.h */
+/* Author: AndreaCatania */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SOFT_PHYSICS_BODY_H
+#define SOFT_PHYSICS_BODY_H
+
+#include "scene/3d/mesh_instance.h"
+
+class SoftBody;
+
+class SoftBodyVisualServerHandler {
+
+ friend class SoftBody;
+
+ RID mesh;
+ int surface;
+ PoolVector<uint8_t> buffer;
+ uint32_t stride;
+ uint32_t offset_vertices;
+ uint32_t offset_normal;
+
+ PoolVector<uint8_t>::Write write_buffer;
+
+private:
+ SoftBodyVisualServerHandler();
+ bool is_ready() { return mesh.is_valid(); }
+ void prepare(RID p_mesh_rid, int p_surface);
+ void clear();
+ void open();
+ void close();
+ void commit_changes();
+
+public:
+ void set_vertex(int p_vertex_id, const void *p_vector3);
+ void set_normal(int p_vertex_id, const void *p_vector3);
+ void set_aabb(const AABB &p_aabb);
+};
+
+class SoftBody : public MeshInstance {
+ GDCLASS(SoftBody, MeshInstance);
+
+public:
+ struct PinnedPoint {
+ int point_index;
+ NodePath spatial_attachment_path;
+ Spatial *spatial_attachment; // Cache
+ /// This is the offset from the soft body to point or attachment to point
+ /// Depend if the spatial_attachment_node is NULL or not
+ Transform vertex_offset_transform; // Cache
+
+ PinnedPoint();
+ PinnedPoint(const PinnedPoint &obj_tocopy);
+ };
+
+private:
+ SoftBodyVisualServerHandler visual_server_handler;
+
+ RID physics_rid;
+
+ bool mesh_owner;
+ uint32_t collision_mask;
+ uint32_t collision_layer;
+ NodePath parent_collision_ignore;
+ PoolVector<PinnedPoint> pinned_points_indices;
+ bool simulation_started;
+ bool pinned_points_cache_dirty;
+
+ Ref<ArrayMesh> debug_mesh_cache;
+ class MeshInstance *debug_mesh;
+
+ bool capture_input_on_drag;
+ bool ray_pickable;
+
+ void _update_pickable();
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ bool _set_property_pinned_points_indices(const Array &p_indices);
+ bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value);
+ bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const;
+
+ virtual void _changed_callback(Object *p_changed, const char *p_prop);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ virtual String get_configuration_warning() const;
+
+protected:
+ void _draw_soft_mesh();
+
+public:
+ void update_physics_server();
+ void become_mesh_owner();
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_layer(uint32_t p_layer);
+ uint32_t get_collision_layer() const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
+ void set_collision_layer_bit(int p_bit, bool p_value);
+ bool get_collision_layer_bit(int p_bit) const;
+
+ void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore);
+ const NodePath &get_parent_collision_ignore() const;
+
+ void set_pinned_points_indices(PoolVector<PinnedPoint> p_pinned_points_indices);
+ PoolVector<PinnedPoint> get_pinned_points_indices();
+
+ void set_simulation_precision(int p_simulation_precision);
+ int get_simulation_precision();
+
+ void set_total_mass(real_t p_total_mass);
+ real_t get_total_mass();
+
+ void set_linear_stiffness(real_t p_linear_stiffness);
+ real_t get_linear_stiffness();
+
+ void set_areaAngular_stiffness(real_t p_areaAngular_stiffness);
+ real_t get_areaAngular_stiffness();
+
+ void set_volume_stiffness(real_t p_volume_stiffness);
+ real_t get_volume_stiffness();
+
+ void set_pressure_coefficient(real_t p_pressure_coefficient);
+ real_t get_pressure_coefficient();
+
+ void set_pose_matching_coefficient(real_t p_pose_matching_coefficient);
+ real_t get_pose_matching_coefficient();
+
+ void set_damping_coefficient(real_t p_damping_coefficient);
+ real_t get_damping_coefficient();
+
+ void set_drag_coefficient(real_t p_drag_coefficient);
+ real_t get_drag_coefficient();
+
+ void add_collision_exception_with(Node *p_node);
+ void remove_collision_exception_with(Node *p_node);
+
+ Vector3 get_point_transform(int p_point_index);
+
+ void pin_point_toggle(int p_point_index);
+ void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath());
+ bool is_point_pinned(int p_point_index) const;
+
+ void set_ray_pickable(bool p_ray_pickable);
+ bool is_ray_pickable() const;
+
+ SoftBody();
+ ~SoftBody();
+
+private:
+ void reset_softbody_pin();
+ void _update_cache_pin_points_datas();
+ void _pin_point_on_physics_server(int p_point_index, bool pin);
+ void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path);
+ void _remove_pinned_point(int p_point_index);
+ int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const;
+ int _has_pinned_point(int p_point_index) const;
+};
+
+#endif // SOFT_PHYSICS_BODY_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 90cb475a7b..cccd1bd197 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -337,10 +337,6 @@ void TextEdit::_update_scrollbars() {
int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1;
int visible_rows = get_visible_rows();
- int first_vis_line = get_first_visible_line();
- int wi;
- int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi));
-
int total_rows = get_total_visible_rows();
if (scroll_past_end_of_file_enabled) {
total_rows += visible_rows - 1;
@@ -1673,7 +1669,6 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
rows /= get_row_height();
rows += get_v_scroll_offset();
int first_vis_line = get_first_visible_line();
- int last_vis_line = get_last_visible_line();
int row = first_vis_line + Math::floor(rows);
int wrap_index = 0;
@@ -3800,7 +3795,6 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
}
// line ends before hit wrap_at; add this word to the substring
wrap_substring += word_str;
- px += word_px;
lines.push_back(wrap_substring);
return lines;
}
@@ -5519,9 +5513,8 @@ int TextEdit::get_last_visible_line() const {
int TextEdit::get_last_visible_line_wrap_index() const {
int first_vis_line = get_first_visible_line();
- int last_vis_line = 0;
int wi;
- last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi);
return wi;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index ee94b64c20..0d69c037fe 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -199,6 +199,7 @@
#include "scene/3d/remote_transform.h"
#include "scene/3d/room_instance.h"
#include "scene/3d/skeleton.h"
+#include "scene/3d/soft_body.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
@@ -428,6 +429,7 @@ void register_scene_types() {
ClassDB::register_class<KinematicCollision>();
ClassDB::register_class<KinematicBody>();
ClassDB::register_class<PhysicalBone>();
+ ClassDB::register_class<SoftBody>();
ClassDB::register_class<VehicleBody>();
ClassDB::register_class<VehicleWheel>();
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index e5d463d391..eb7d517841 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -211,9 +211,9 @@ Error DynamicFontAtSize::_load() {
scale_color_font = float(id.size) / face->available_sizes[i].width;
}
}
- error = FT_Select_Size(face, best_match);
+ FT_Select_Size(face, best_match);
} else {
- error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
+ FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
}
ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index b0620d3363..fe87dcdd2c 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -40,7 +40,6 @@
void Mesh::_clear_triangle_mesh() const {
triangle_mesh.unref();
- ;
}
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
@@ -110,6 +109,54 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
return triangle_mesh;
}
+void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) {
+
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ if (tm.is_null())
+ return;
+
+ PoolVector<int> triangle_indices;
+ tm->get_indices(&triangle_indices);
+ const int triangles_num = tm->get_triangles().size();
+ PoolVector<Vector3> vertices = tm->get_vertices();
+
+ r_lines.resize(tm->get_triangles().size() * 6); // 3 lines x 2 points each line
+
+ PoolVector<int>::Read ind_r = triangle_indices.read();
+ PoolVector<Vector3>::Read ver_r = vertices.read();
+ for (int j = 0, x = 0, i = 0; i < triangles_num; j += 6, x += 3, ++i) {
+ // Triangle line 1
+ r_lines[j + 0] = ver_r[ind_r[x + 0]];
+ r_lines[j + 1] = ver_r[ind_r[x + 1]];
+
+ // Triangle line 2
+ r_lines[j + 2] = ver_r[ind_r[x + 1]];
+ r_lines[j + 3] = ver_r[ind_r[x + 2]];
+
+ // Triangle line 3
+ r_lines[j + 4] = ver_r[ind_r[x + 2]];
+ r_lines[j + 5] = ver_r[ind_r[x + 0]];
+ }
+}
+void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) {
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ if (tm.is_null())
+ return;
+
+ PoolVector<Vector3> vertices = tm->get_vertices();
+
+ int vertices_size = vertices.size();
+ r_points.resize(vertices_size);
+ for (int i = 0; i < vertices_size; ++i) {
+ r_points[i] = vertices[i];
+ }
+}
+
+bool Mesh::surface_is_softbody_friendly(int p_idx) const {
+ const uint32_t surface_format = surface_get_format(p_idx);
+ return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_VERTEX)) && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL)));
+}
+
PoolVector<Face3> Mesh::get_faces() const {
Ref<TriangleMesh> tm = generate_triangle_mesh();
@@ -484,6 +531,10 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_MAX);
}
+void Mesh::clear_cache() {
+ _clear_triangle_mesh();
+}
+
Mesh::Mesh() {
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index a3fb068569..2127eaae4c 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -123,6 +123,7 @@ public:
virtual int get_surface_count() const = 0;
virtual int surface_get_array_len(int p_idx) const = 0;
virtual int surface_get_array_index_len(int p_idx) const = 0;
+ virtual bool surface_is_softbody_friendly(int p_idx) const;
virtual Array surface_get_arrays(int p_surface) const = 0;
virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0;
virtual uint32_t surface_get_format(int p_idx) const = 0;
@@ -133,6 +134,8 @@ public:
PoolVector<Face3> get_faces() const;
Ref<TriangleMesh> generate_triangle_mesh() const;
+ void generate_debug_mesh_lines(Vector<Vector3> &r_lines);
+ void generate_debug_mesh_indices(Vector<Vector3> &r_points);
Ref<Shape> create_trimesh_shape() const;
Ref<Shape> create_convex_shape() const;
@@ -143,6 +146,7 @@ public:
void set_lightmap_size_hint(const Vector2 &p_size);
Size2 get_lightmap_size_hint() const;
+ void clear_cache();
Mesh();
};
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 5a42873d79..3df9ab26a4 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -421,6 +421,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_
Array a = commit_to_arrays();
mesh->add_surface_from_arrays(primitive, a, Array(), p_flags);
+
if (material.is_valid())
mesh->surface_set_material(surface, material);
diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp
index 9faa4056c3..3414351681 100644
--- a/servers/audio/audio_rb_resampler.cpp
+++ b/servers/audio/audio_rb_resampler.cpp
@@ -100,6 +100,8 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i
if (C == 6) {
+ // FIXME: Lot of unused assignments here, but it seems like intermediate calculations
+ // should be done as for C == 2 (C == 4 also has some unused assignments).
float v0 = rb[(pos * 6) + 0];
float v1 = rb[(pos * 6) + 1];
float v2 = rb[(pos * 6) + 2];
diff --git a/servers/physics/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp
index e587485fcb..44b7c9ac34 100644
--- a/servers/physics/collision_solver_sat.cpp
+++ b/servers/physics/collision_solver_sat.cpp
@@ -217,8 +217,6 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_
// 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++) {
real_t d = plane_B.distance_to(clipbuf_src[i]);
@@ -233,7 +231,6 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_
continue;
p_callback->call(clipbuf_src[i], closest_B);
- added++;
}
}
diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h
index d8c73abb16..7e9700d026 100644
--- a/servers/physics/physics_server_sw.h
+++ b/servers/physics/physics_server_sw.h
@@ -234,6 +234,72 @@ public:
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body);
+ /* SOFT BODY */
+
+ virtual RID soft_body_create(bool p_init_sleeping = false) { return RID(); }
+
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) {}
+
+ virtual void soft_body_set_space(RID p_body, RID p_space) {}
+ virtual RID soft_body_get_space(RID p_body) const { return RID(); }
+
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {}
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const { return 0; }
+
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) {}
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const { return 0; }
+
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) {}
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) {}
+ virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {}
+
+ virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {}
+ virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const { return Variant(); }
+
+ virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) {}
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const { return Vector3(); }
+
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) {}
+ virtual bool soft_body_is_ray_pickable(RID p_body) const { return false; }
+
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {}
+ virtual int soft_body_get_simulation_precision(RID p_body) { return 0; }
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) {}
+ virtual real_t soft_body_get_total_mass(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) {}
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) {}
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {}
+ virtual real_t soft_body_get_volume_stiffness(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {}
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {}
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {}
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {}
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) { return 0.; }
+
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) {}
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {}
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) { return Vector3(); }
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const { return Vector3(); }
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body) {}
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {}
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) { return 0; }
+
/* JOINT API */
virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B);
diff --git a/servers/physics_server.h b/servers/physics_server.h
index 91e7f58319..497d23c555 100644
--- a/servers/physics_server.h
+++ b/servers/physics_server.h
@@ -490,6 +490,72 @@ public:
virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL) = 0;
+ /* SOFT BODY */
+
+ virtual RID soft_body_create(bool p_init_sleeping = false) = 0;
+
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) = 0;
+
+ virtual void soft_body_set_space(RID p_body, RID p_space) = 0;
+ virtual RID soft_body_get_space(RID p_body) const = 0;
+
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) = 0;
+
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) = 0;
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const = 0;
+
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) = 0;
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const = 0;
+
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) = 0;
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) = 0;
+ virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) = 0;
+
+ virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) = 0;
+ virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const = 0;
+
+ virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) = 0;
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const = 0;
+
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) = 0;
+ virtual bool soft_body_is_ray_pickable(RID p_body) const = 0;
+
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) = 0;
+ virtual int soft_body_get_simulation_precision(RID p_body) = 0;
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) = 0;
+ virtual real_t soft_body_get_total_mass(RID p_body) = 0;
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) = 0;
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) = 0;
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) = 0;
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) = 0;
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) = 0;
+ virtual real_t soft_body_get_volume_stiffness(RID p_body) = 0;
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) = 0;
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) = 0;
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) = 0;
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) = 0;
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) = 0;
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) = 0;
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) = 0;
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const = 0;
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body) = 0;
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) = 0;
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) = 0;
+
/* JOINT API */
enum JointType {
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 731a61890c..a8f4377ce7 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -280,6 +280,7 @@ public:
virtual AABB mesh_get_custom_aabb(RID p_mesh) const = 0;
virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton) const = 0;
+
virtual void mesh_clear(RID p_mesh) = 0;
/* MULTIMESH API */
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 143e56b848..1f3319dc04 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -187,16 +187,14 @@ RID VisualServer::_make_test_cube() {
PoolVector<float> tangents;
PoolVector<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++;
+#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));
for (int i = 0; i < 6; i++) {
@@ -795,6 +793,140 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_
return OK;
}
+uint32_t VisualServer::mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_index_len, int p_array_index) const {
+ uint32_t offsets[ARRAY_MAX];
+ mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets);
+ return offsets[p_array_index];
+}
+
+uint32_t VisualServer::mesh_surface_get_format_stride(uint32_t p_format, int p_vertex_len, int p_index_len) const {
+ uint32_t offsets[ARRAY_MAX];
+ return mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets);
+}
+
+uint32_t VisualServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets) const {
+
+ int total_elem_size = 0;
+
+ for (int i = 0; i < VS::ARRAY_MAX; i++) {
+
+ r_offsets[i] = 0; //reset
+
+ if (!(p_format & (1 << i))) // no array
+ continue;
+
+ int elem_size = 0;
+
+ switch (i) {
+
+ case VS::ARRAY_VERTEX: {
+
+ if (p_format & ARRAY_FLAG_USE_2D_VERTICES) {
+ elem_size = 2;
+ } else {
+ elem_size = 3;
+ }
+
+ if (p_format & ARRAY_COMPRESS_VERTEX) {
+ elem_size *= sizeof(int16_t);
+ } else {
+ elem_size *= sizeof(float);
+ }
+
+ if (elem_size == 6) {
+ elem_size = 8;
+ }
+
+ } break;
+ case VS::ARRAY_NORMAL: {
+
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 3;
+ }
+
+ } break;
+
+ case VS::ARRAY_TANGENT: {
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
+
+ } break;
+ case VS::ARRAY_COLOR: {
+
+ if (p_format & ARRAY_COMPRESS_COLOR) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
+ } break;
+ case VS::ARRAY_TEX_UV: {
+ if (p_format & ARRAY_COMPRESS_TEX_UV) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 2;
+ }
+
+ } break;
+
+ case VS::ARRAY_TEX_UV2: {
+ if (p_format & ARRAY_COMPRESS_TEX_UV2) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 2;
+ }
+
+ } break;
+ case VS::ARRAY_WEIGHTS: {
+
+ if (p_format & ARRAY_COMPRESS_WEIGHTS) {
+ elem_size = sizeof(uint16_t) * 4;
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
+
+ } break;
+ case VS::ARRAY_BONES: {
+
+ if (p_format & ARRAY_FLAG_USE_16_BIT_BONES) {
+ elem_size = sizeof(uint16_t) * 4;
+ } else {
+ elem_size = sizeof(uint32_t);
+ }
+
+ } break;
+ case VS::ARRAY_INDEX: {
+
+ if (p_index_len <= 0) {
+ ERR_PRINT("index_array_len==NO_INDEX_ARRAY");
+ break;
+ }
+ /* determine whether using 16 or 32 bits indices */
+ if (p_vertex_len >= (1 << 16)) {
+
+ elem_size = 4;
+
+ } else {
+ elem_size = 2;
+ }
+ r_offsets[i] = elem_size;
+ continue;
+ } break;
+ default: {
+ ERR_FAIL_V(0);
+ }
+ }
+
+ r_offsets[i] = total_elem_size;
+ total_elem_size += elem_size;
+ }
+ return total_elem_size;
+}
+
void VisualServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) {
ERR_FAIL_INDEX(p_primitive, VS::PRIMITIVE_MAX);
@@ -1564,11 +1696,14 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("material_set_next_pass", "material", "next_material"), &VisualServer::material_set_next_pass);
ClassDB::bind_method(D_METHOD("mesh_create"), &VisualServer::mesh_create);
+ ClassDB::bind_method(D_METHOD("mesh_surface_get_format_offset", "format", "vertex_len", "index_len", "array_index"), &VisualServer::mesh_surface_get_format_offset);
+ ClassDB::bind_method(D_METHOD("mesh_surface_get_format_stride", "format", "vertex_len", "index_len"), &VisualServer::mesh_surface_get_format_stride);
ClassDB::bind_method(D_METHOD("mesh_add_surface_from_arrays", "mesh", "primtive", "arrays", "blend_shapes", "compress_format"), &VisualServer::mesh_add_surface_from_arrays, DEFVAL(Array()), DEFVAL(ARRAY_COMPRESS_DEFAULT));
ClassDB::bind_method(D_METHOD("mesh_set_blend_shape_count", "mesh", "amount"), &VisualServer::mesh_set_blend_shape_count);
ClassDB::bind_method(D_METHOD("mesh_get_blend_shape_count", "mesh"), &VisualServer::mesh_get_blend_shape_count);
ClassDB::bind_method(D_METHOD("mesh_set_blend_shape_mode", "mesh", "mode"), &VisualServer::mesh_set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("mesh_get_blend_shape_mode", "mesh"), &VisualServer::mesh_get_blend_shape_mode);
+ ClassDB::bind_method(D_METHOD("mesh_surface_update_region", "mesh", "surface", "offset", "data"), &VisualServer::mesh_surface_update_region);
ClassDB::bind_method(D_METHOD("mesh_surface_set_material", "mesh", "surface", "material"), &VisualServer::mesh_surface_set_material);
ClassDB::bind_method(D_METHOD("mesh_surface_get_material", "mesh", "surface"), &VisualServer::mesh_surface_get_material);
ClassDB::bind_method(D_METHOD("mesh_surface_get_array_len", "mesh", "surface"), &VisualServer::mesh_surface_get_array_len);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index a76cad24a2..367642b7d4 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -251,6 +251,10 @@ public:
virtual RID mesh_create() = 0;
+ virtual uint32_t mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_index_len, int p_array_index) const;
+ virtual uint32_t mesh_surface_get_format_stride(uint32_t p_format, int p_vertex_len, int p_index_len) const;
+ /// Returns stride
+ virtual uint32_t mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets) const;
virtual void mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), uint32_t p_compress_format = ARRAY_COMPRESS_DEFAULT);
virtual void mesh_add_surface(RID p_mesh, uint32_t p_format, PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>()) = 0;