summaryrefslogtreecommitdiff
path: root/scene/resources
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources')
-rw-r--r--scene/resources/animation.cpp1217
-rw-r--r--scene/resources/animation.h89
-rw-r--r--scene/resources/audio_stream_sample.cpp4
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/bit_map.cpp2
-rw-r--r--scene/resources/canvas_item_material.cpp5
-rw-r--r--scene/resources/canvas_item_material.h1
-rw-r--r--scene/resources/curve.cpp8
-rw-r--r--scene/resources/default_theme/SCsub2
-rw-r--r--scene/resources/default_theme/default_theme.cpp59
-rw-r--r--scene/resources/environment.cpp21
-rw-r--r--scene/resources/environment.h3
-rw-r--r--scene/resources/font.cpp86
-rw-r--r--scene/resources/font.h8
-rw-r--r--scene/resources/immediate_mesh.cpp3
-rw-r--r--scene/resources/immediate_mesh.h1
-rw-r--r--scene/resources/importer_mesh.cpp1247
-rw-r--r--scene/resources/importer_mesh.h133
-rw-r--r--scene/resources/material.cpp35
-rw-r--r--scene/resources/material.h4
-rw-r--r--scene/resources/mesh.cpp116
-rw-r--r--scene/resources/mesh.h39
-rw-r--r--scene/resources/mesh_library.cpp33
-rw-r--r--scene/resources/mesh_library.h3
-rw-r--r--scene/resources/navigation_mesh.cpp10
-rw-r--r--scene/resources/navigation_mesh.h2
-rw-r--r--scene/resources/packed_scene.cpp43
-rw-r--r--scene/resources/particles_material.cpp3
-rw-r--r--scene/resources/particles_material.h1
-rw-r--r--scene/resources/primitive_meshes.cpp8
-rw-r--r--scene/resources/primitive_meshes.h2
-rw-r--r--scene/resources/resource_format_text.cpp42
-rw-r--r--scene/resources/shader.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d.cpp42
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp2
-rw-r--r--scene/resources/skeleton_modification_3d.cpp40
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp82
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h2
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp7
-rw-r--r--scene/resources/skeleton_modification_3d_lookat.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp28
-rw-r--r--scene/resources/skeleton_modification_stack_2d.cpp2
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp2
-rw-r--r--scene/resources/sky_material.cpp26
-rw-r--r--scene/resources/sky_material.h3
-rw-r--r--scene/resources/sprite_frames.cpp20
-rw-r--r--scene/resources/surface_tool.cpp52
-rw-r--r--scene/resources/text_line.cpp18
-rw-r--r--scene/resources/text_line.h10
-rw-r--r--scene/resources/text_paragraph.cpp36
-rw-r--r--scene/resources/text_paragraph.h12
-rw-r--r--scene/resources/texture.cpp32
-rw-r--r--scene/resources/texture.h4
-rw-r--r--scene/resources/theme.cpp944
-rw-r--r--scene/resources/theme.h17
-rw-r--r--scene/resources/tile_set.cpp1747
-rw-r--r--scene/resources/tile_set.h233
-rw-r--r--scene/resources/visual_shader.cpp104
-rw-r--r--scene/resources/visual_shader.h2
-rw-r--r--scene/resources/visual_shader_nodes.cpp2
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp20
-rw-r--r--scene/resources/visual_shader_particle_nodes.h5
-rw-r--r--scene/resources/world_boundary_shape_2d.cpp (renamed from scene/resources/world_margin_shape_2d.cpp)36
-rw-r--r--scene/resources/world_boundary_shape_2d.h (renamed from scene/resources/world_margin_shape_2d.h)16
-rw-r--r--scene/resources/world_boundary_shape_3d.cpp (renamed from scene/resources/world_margin_shape_3d.cpp)22
-rw-r--r--scene/resources/world_boundary_shape_3d.h (renamed from scene/resources/world_margin_shape_3d.h)18
67 files changed, 5058 insertions, 1772 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index b4eec2530b..08b78a39b1 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -43,8 +43,14 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (tracks.size() == track && what == "type") {
String type = p_value;
- if (type == "transform" || type == "transform3d") {
- add_track(TYPE_TRANSFORM3D);
+ if (type == "position_3d") {
+ add_track(TYPE_POSITION_3D);
+ } else if (type == "rotation_3d") {
+ add_track(TYPE_ROTATION_3D);
+ } else if (type == "scale_3d") {
+ add_track(TYPE_SCALE_3D);
+ } else if (type == "blend_shape") {
+ add_track(TYPE_BLEND_SHAPE);
} else if (type == "value") {
add_track(TYPE_VALUE);
} else if (type == "method") {
@@ -75,35 +81,91 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "enabled") {
track_set_enabled(track, p_value);
} else if (what == "keys" || what == "key_values") {
- if (track_get_type(track) == TYPE_TRANSFORM3D) {
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
+ if (track_get_type(track) == TYPE_POSITION_3D) {
+ PositionTrack *tt = static_cast<PositionTrack *>(tracks[track]);
Vector<real_t> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false);
+ ERR_FAIL_COND_V(vcount % POSITION_TRACK_SIZE, false);
const real_t *r = values.ptr();
- int64_t count = vcount / TRANSFORM_TRACK_SIZE;
- tt->transforms.resize(count);
+ int64_t count = vcount / POSITION_TRACK_SIZE;
+ tt->positions.resize(count);
+ TKey<Vector3> *tw = tt->positions.ptrw();
for (int i = 0; i < count; i++) {
- TKey<TransformKey> &tk = tt->transforms.write[i];
- const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE];
+ TKey<Vector3> &tk = tw[i];
+ const real_t *ofs = &r[i * POSITION_TRACK_SIZE];
tk.time = ofs[0];
tk.transition = ofs[1];
- tk.value.loc.x = ofs[2];
- tk.value.loc.y = ofs[3];
- tk.value.loc.z = ofs[4];
+ tk.value.x = ofs[2];
+ tk.value.y = ofs[3];
+ tk.value.z = ofs[4];
+ }
+ } else if (track_get_type(track) == TYPE_ROTATION_3D) {
+ RotationTrack *rt = static_cast<RotationTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % ROTATION_TRACK_SIZE, false);
+
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / ROTATION_TRACK_SIZE;
+ rt->rotations.resize(count);
+
+ TKey<Quaternion> *rw = rt->rotations.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<Quaternion> &rk = rw[i];
+ const real_t *ofs = &r[i * ROTATION_TRACK_SIZE];
+ rk.time = ofs[0];
+ rk.transition = ofs[1];
+
+ rk.value.x = ofs[2];
+ rk.value.y = ofs[3];
+ rk.value.z = ofs[4];
+ rk.value.w = ofs[5];
+ }
+ } else if (track_get_type(track) == TYPE_SCALE_3D) {
+ ScaleTrack *st = static_cast<ScaleTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % SCALE_TRACK_SIZE, false);
+
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / SCALE_TRACK_SIZE;
+ st->scales.resize(count);
- tk.value.rot.x = ofs[5];
- tk.value.rot.y = ofs[6];
- tk.value.rot.z = ofs[7];
- tk.value.rot.w = ofs[8];
+ TKey<Vector3> *sw = st->scales.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<Vector3> &sk = sw[i];
+ const real_t *ofs = &r[i * SCALE_TRACK_SIZE];
+ sk.time = ofs[0];
+ sk.transition = ofs[1];
+
+ sk.value.x = ofs[2];
+ sk.value.y = ofs[3];
+ sk.value.z = ofs[4];
+ }
+ } else if (track_get_type(track) == TYPE_BLEND_SHAPE) {
+ BlendShapeTrack *st = static_cast<BlendShapeTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % BLEND_SHAPE_TRACK_SIZE, false);
+
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / BLEND_SHAPE_TRACK_SIZE;
+ st->blend_shapes.resize(count);
- tk.value.scale.x = ofs[9];
- tk.value.scale.y = ofs[10];
- tk.value.scale.z = ofs[11];
+ TKey<float> *sw = st->blend_shapes.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<float> &sk = sw[i];
+ const real_t *ofs = &r[i * BLEND_SHAPE_TRACK_SIZE];
+ sk.time = ofs[0];
+ sk.transition = ofs[1];
+ sk.value = ofs[2];
}
} else if (track_get_type(track) == TYPE_VALUE) {
@@ -319,8 +381,17 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
ERR_FAIL_INDEX_V(track, tracks.size(), false);
if (what == "type") {
switch (track_get_type(track)) {
- case TYPE_TRANSFORM3D:
- r_ret = "transform";
+ case TYPE_POSITION_3D:
+ r_ret = "position_3d";
+ break;
+ case TYPE_ROTATION_3D:
+ r_ret = "rotation_3d";
+ break;
+ case TYPE_SCALE_3D:
+ r_ret = "scale_3d";
+ break;
+ case TYPE_BLEND_SHAPE:
+ r_ret = "blend_shape";
break;
case TYPE_VALUE:
r_ret = "value";
@@ -352,31 +423,64 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "enabled") {
r_ret = track_is_enabled(track);
} else if (what == "keys") {
- if (track_get_type(track) == TYPE_TRANSFORM3D) {
+ if (track_get_type(track) == TYPE_POSITION_3D) {
Vector<real_t> keys;
int kk = track_get_key_count(track);
- keys.resize(kk * TRANSFORM_TRACK_SIZE);
+ keys.resize(kk * POSITION_TRACK_SIZE);
real_t *w = keys.ptrw();
int idx = 0;
for (int i = 0; i < track_get_key_count(track); i++) {
Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- transform_track_get_key(track, i, &loc, &rot, &scale);
+ position_track_get_key(track, i, &loc);
w[idx++] = track_get_key_time(track, i);
w[idx++] = track_get_key_transition(track, i);
w[idx++] = loc.x;
w[idx++] = loc.y;
w[idx++] = loc.z;
+ }
+ r_ret = keys;
+ return true;
+ } else if (track_get_type(track) == TYPE_ROTATION_3D) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * ROTATION_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ Quaternion rot;
+ rotation_track_get_key(track, i, &rot);
+
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
w[idx++] = rot.x;
w[idx++] = rot.y;
w[idx++] = rot.z;
w[idx++] = rot.w;
+ }
+ r_ret = keys;
+ return true;
+
+ } else if (track_get_type(track) == TYPE_SCALE_3D) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * SCALE_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ Vector3 scale;
+ scale_track_get_key(track, i, &scale);
+
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
w[idx++] = scale.x;
w[idx++] = scale.y;
w[idx++] = scale.z;
@@ -384,7 +488,25 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = keys;
return true;
+ } else if (track_get_type(track) == TYPE_BLEND_SHAPE) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * BLEND_SHAPE_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ float bs;
+ blend_shape_track_get_key(track, i, &bs);
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
+ w[idx++] = bs;
+ }
+
+ r_ret = keys;
+ return true;
} else if (track_get_type(track) == TYPE_VALUE) {
const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]);
@@ -591,10 +713,22 @@ int Animation::add_track(TrackType p_type, int p_at_pos) {
}
switch (p_type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = memnew(TransformTrack);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = memnew(PositionTrack);
tracks.insert(p_at_pos, tt);
} break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = memnew(RotationTrack);
+ tracks.insert(p_at_pos, rt);
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = memnew(ScaleTrack);
+ tracks.insert(p_at_pos, st);
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = memnew(BlendShapeTrack);
+ tracks.insert(p_at_pos, bst);
+ } break;
case TYPE_VALUE: {
tracks.insert(p_at_pos, memnew(ValueTrack));
@@ -629,9 +763,24 @@ void Animation::remove_track(int p_track) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- _clear(tt->transforms);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ _clear(tt->positions);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ _clear(rt->rotations);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ _clear(st->scales);
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ _clear(bst->blend_shapes);
} break;
case TYPE_VALUE: {
@@ -672,7 +821,7 @@ int Animation::get_track_count() const {
}
Animation::TrackType Animation::track_get_type(int p_track) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D);
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_VALUE);
return tracks[p_track]->type;
}
@@ -720,31 +869,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const {
return tracks[p_track]->loop_wrap;
}
-// transform
-/*
-template<class T>
-int Animation::_insert_pos(double p_time, T& p_keys) {
- // simple, linear time inset that should be fast enough in reality.
-
- int idx=p_keys.size();
-
- while(true) {
-
-
- if (idx==0 || p_keys[idx-1].time < p_time) {
- //condition for insertion.
- p_keys.insert(idx,T());
- return idx;
- } else if (p_keys[idx-1].time == p_time) {
- // condition for replacing.
- return idx-1;
- }
-
- idx--;
- }
-}
-
-*/
template <class T, class V>
int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
int idx = p_keys.size();
@@ -774,45 +898,200 @@ void Animation::_clear(T &p_keys) {
p_keys.clear();
}
-Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
+////
+
+int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, -1);
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ TKey<Vector3> tkey;
+ tkey.time = p_time;
+ tkey.value = p_position;
+
+ int ret = _insert(p_time, tt->positions, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_position) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
- ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER);
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER);
- if (r_loc) {
- *r_loc = tt->transforms[p_key].value.loc;
- }
- if (r_rot) {
- *r_rot = tt->transforms[p_key].value.rot;
+ *r_position = tt->positions[p_key].value;
+
+ return OK;
+}
+
+Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ bool ok = false;
+
+ Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
}
- if (r_scale) {
- *r_scale = tt->transforms[p_key].value.scale;
+ *r_interpolation = tk;
+ return OK;
+}
+
+////
+
+int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, -1);
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ TKey<Quaternion> tkey;
+ tkey.time = p_time;
+ tkey.value = p_rotation;
+
+ int ret = _insert(p_time, rt->rotations, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER);
+
+ *r_rotation = rt->rotations[p_key].value;
+
+ return OK;
+}
+
+Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ bool ok = false;
+
+ Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
}
+ *r_interpolation = tk;
+ return OK;
+}
+
+////
+
+int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, -1);
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+
+ TKey<Vector3> tkey;
+ tkey.time = p_time;
+ tkey.value = p_scale;
+
+ int ret = _insert(p_time, st->scales, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER);
+
+ *r_scale = st->scales[p_key].value;
+
+ return OK;
+}
+
+Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+
+ bool ok = false;
+ Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *r_interpolation = tk;
return OK;
}
-int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) {
+int Animation::blend_shape_track_insert_key(int p_track, double p_time, float p_blend_shape) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1);
+ ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, -1);
- TransformTrack *tt = static_cast<TransformTrack *>(t);
+ BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
- TKey<TransformKey> tkey;
+ TKey<float> tkey;
tkey.time = p_time;
- tkey.value.loc = p_loc;
- tkey.value.rot = p_rot;
- tkey.value.scale = p_scale;
+ tkey.value = p_blend_shape;
- int ret = _insert(p_time, tt->transforms, tkey);
+ int ret = _insert(p_time, st->blend_shapes, tkey);
emit_changed();
return ret;
}
+Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blend_shape) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, st->blend_shapes.size(), ERR_INVALID_PARAMETER);
+
+ *r_blend_shape = st->blend_shapes[p_key].value;
+
+ return OK;
+}
+
+Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
+
+ BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
+
+ bool ok = false;
+
+ float tk = _interpolate(st->blend_shapes, p_time, st->interpolation, st->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *r_interpolation = tk;
+ return OK;
+}
+
void Animation::track_remove_key_at_time(int p_track, double p_time) {
int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
@@ -824,10 +1103,28 @@ void Animation::track_remove_key(int p_track, int p_idx) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_idx, tt->transforms.size());
- tt->transforms.remove(p_idx);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, tt->positions.size());
+ tt->positions.remove(p_idx);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, rt->rotations.size());
+ rt->rotations.remove(p_idx);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, st->scales.size());
+ st->scales.remove(p_idx);
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size());
+ bst->blend_shapes.remove(p_idx);
} break;
case TYPE_VALUE: {
@@ -870,13 +1167,49 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- int k = _find(tt->transforms, p_time);
- if (k < 0 || k >= tt->transforms.size()) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ int k = _find(tt->positions, p_time);
+ if (k < 0 || k >= tt->positions.size()) {
+ return -1;
+ }
+ if (tt->positions[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ int k = _find(rt->rotations, p_time);
+ if (k < 0 || k >= rt->rotations.size()) {
+ return -1;
+ }
+ if (rt->rotations[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ int k = _find(st->scales, p_time);
+ if (k < 0 || k >= st->scales.size()) {
+ return -1;
+ }
+ if (st->scales[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ int k = _find(bst->blend_shapes, p_time);
+ if (k < 0 || k >= bst->blend_shapes.size()) {
return -1;
}
- if (tt->transforms[k].time != p_time && p_exact) {
+ if (bst->blend_shapes[k].time != p_time && p_exact) {
return -1;
}
return k;
@@ -952,24 +1285,27 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- Dictionary d = p_key;
- Vector3 loc;
- if (d.has("location")) {
- loc = d["location"];
- }
+ case TYPE_POSITION_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I));
+ int idx = position_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- Quaternion rot;
- if (d.has("rotation")) {
- rot = d["rotation"];
- }
+ } break;
+ case TYPE_ROTATION_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS));
+ int idx = rotation_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- Vector3 scale;
- if (d.has("scale")) {
- scale = d["scale"];
- }
+ } break;
+ case TYPE_SCALE_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I));
+ int idx = scale_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- int idx = transform_track_insert_key(p_track, p_time, loc, rot, scale);
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT));
+ int idx = blend_shape_track_insert_key(p_track, p_time, p_key);
track_set_key_transition(p_track, idx, p_transition);
} break;
@@ -1054,9 +1390,21 @@ int Animation::track_get_key_count(int p_track) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- return tt->transforms.size();
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ return tt->positions.size();
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ return rt->rotations.size();
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ return st->scales.size();
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ return bst->blend_shapes.size();
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1089,16 +1437,29 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant());
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant());
- Dictionary d;
- d["location"] = tt->transforms[p_key_idx].value.loc;
- d["rotation"] = tt->transforms[p_key_idx].value.rot;
- d["scale"] = tt->transforms[p_key_idx].value.scale;
+ return tt->positions[p_key_idx].value;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant());
- return d;
+ return rt->rotations[p_key_idx].value;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant());
+
+ return st->scales[p_key_idx].value;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), Variant());
+
+ return bst->blend_shapes[p_key_idx].value;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1157,10 +1518,25 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
- return tt->transforms[p_key_idx].time;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
+ return tt->positions[p_key_idx].time;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
+ return rt->rotations[p_key_idx].time;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
+ return st->scales[p_key_idx].time;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1);
+ return bst->blend_shapes[p_key_idx].time;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1202,13 +1578,40 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
- TKey<TransformKey> key = tt->transforms[p_key_idx];
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ TKey<Vector3> key = tt->positions[p_key_idx];
+ key.time = p_time;
+ tt->positions.remove(p_key_idx);
+ _insert(p_time, tt->positions, key);
+ return;
+ }
+ case TYPE_ROTATION_3D: {
+ RotationTrack *tt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->rotations.size());
+ TKey<Quaternion> key = tt->rotations[p_key_idx];
+ key.time = p_time;
+ tt->rotations.remove(p_key_idx);
+ _insert(p_time, tt->rotations, key);
+ return;
+ }
+ case TYPE_SCALE_3D: {
+ ScaleTrack *tt = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->scales.size());
+ TKey<Vector3> key = tt->scales[p_key_idx];
key.time = p_time;
- tt->transforms.remove(p_key_idx);
- _insert(p_time, tt->transforms, key);
+ tt->scales.remove(p_key_idx);
+ _insert(p_time, tt->scales, key);
+ return;
+ }
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->blend_shapes.size());
+ TKey<float> key = tt->blend_shapes[p_key_idx];
+ key.time = p_time;
+ tt->blend_shapes.remove(p_key_idx);
+ _insert(p_time, tt->blend_shapes, key);
return;
}
case TYPE_VALUE: {
@@ -1266,10 +1669,25 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
- return tt->transforms[p_key_idx].transition;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
+ return tt->positions[p_key_idx].transition;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
+ return rt->rotations[p_key_idx].transition;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
+ return st->scales[p_key_idx].transition;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1);
+ return bst->blend_shapes[p_key_idx].transition;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1302,21 +1720,36 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+ case TYPE_POSITION_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
- Dictionary d = p_value;
+ tt->positions.write[p_key_idx].value = p_value;
- if (d.has("location")) {
- tt->transforms.write[p_key_idx].value.loc = d["location"];
- }
- if (d.has("rotation")) {
- tt->transforms.write[p_key_idx].value.rot = d["rotation"];
- }
- if (d.has("scale")) {
- tt->transforms.write[p_key_idx].value.scale = d["scale"];
- }
+ } break;
+ case TYPE_ROTATION_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS));
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
+
+ rt->rotations.write[p_key_idx].value = p_value;
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, st->scales.size());
+
+ st->scales.write[p_key_idx].value = p_value;
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::FLOAT) && (p_value.get_type() != Variant::INT));
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size());
+
+ bst->blend_shapes.write[p_key_idx].value = p_value;
} break;
case TYPE_VALUE: {
@@ -1385,10 +1818,25 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
- tt->transforms.write[p_key_idx].transition = p_transition;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ tt->positions.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
+ rt->rotations.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, st->scales.size());
+ st->scales.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size());
+ bst->blend_shapes.write[p_key_idx].transition = p_transition;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1450,15 +1898,6 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
return middle;
}
-Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const {
- TransformKey ret;
- ret.loc = _interpolate(p_a.loc, p_b.loc, p_c);
- ret.rot = _interpolate(p_a.rot, p_b.rot, p_c);
- ret.scale = _interpolate(p_a.scale, p_b.scale, p_c);
-
- return ret;
-}
-
Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const {
return p_a.lerp(p_b, p_c);
}
@@ -1477,16 +1916,6 @@ real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c)
return p_a * (1.0 - p_c) + p_b * p_c;
}
-Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const {
- Animation::TransformKey tk;
-
- tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c);
- tk.scale = p_a.scale.cubic_interpolate(p_b.scale, p_pre_a.scale, p_post_b.scale, p_c);
- tk.rot = p_a.rot.cubic_slerp(p_b.rot, p_pre_a.rot, p_post_b.rot, p_c);
-
- return tk;
-}
-
Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const {
return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c);
}
@@ -1729,36 +2158,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
// do a barrel roll
}
-Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
- Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
-
- TransformTrack *tt = static_cast<TransformTrack *>(t);
-
- bool ok = false;
-
- TransformKey tk = _interpolate(tt->transforms, p_time, tt->interpolation, tt->loop_wrap, &ok);
-
- if (!ok) {
- return ERR_UNAVAILABLE;
- }
-
- if (r_loc) {
- *r_loc = tk.loc;
- }
-
- if (r_rot) {
- *r_rot = tk.rot;
- }
-
- if (r_scale) {
- *r_scale = tk.scale;
- }
-
- return OK;
-}
-
Variant Animation::value_track_interpolate(int p_track, double p_time) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
@@ -1933,10 +2332,28 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
// handle loop by splitting
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- const TransformTrack *tt = static_cast<const TransformTrack *>(t);
- _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices);
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
} break;
case TYPE_VALUE: {
@@ -1989,9 +2406,24 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- const TransformTrack *tt = static_cast<const TransformTrack *>(t);
- _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices);
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
} break;
case TYPE_VALUE: {
@@ -2608,7 +3040,11 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled);
ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled);
- ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
+ ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key);
+ ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key);
+ ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
+ ClassDB::bind_method(D_METHOD("blend_shape_track_insert_key", "track_idx", "time", "amount"), &Animation::blend_shape_track_insert_key);
+
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
@@ -2628,7 +3064,6 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap);
ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap);
- ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate);
ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
@@ -2682,7 +3117,10 @@ void Animation::_bind_methods() {
ADD_SIGNAL(MethodInfo("tracks_changed"));
BIND_ENUM_CONSTANT(TYPE_VALUE);
- BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D);
+ BIND_ENUM_CONSTANT(TYPE_POSITION_3D);
+ BIND_ENUM_CONSTANT(TYPE_ROTATION_3D);
+ BIND_ENUM_CONSTANT(TYPE_SCALE_3D);
+ BIND_ENUM_CONSTANT(TYPE_BLEND_SHAPE);
BIND_ENUM_CONSTANT(TYPE_METHOD);
BIND_ENUM_CONSTANT(TYPE_BEZIER);
BIND_ENUM_CONSTANT(TYPE_AUDIO);
@@ -2709,193 +3147,285 @@ void Animation::clear() {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) {
- real_t c = (t1.time - t0.time) / (t2.time - t0.time);
- real_t t[3] = { -1, -1, -1 };
+bool Animation::_position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) {
+ const Vector3 &v0 = t0.value;
+ const Vector3 &v1 = t1.value;
+ const Vector3 &v2 = t2.value;
- { //translation
+ if (v0.is_equal_approx(v2)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!v0.is_equal_approx(v1)) {
+ //not close, not optimizable
+ return false;
+ }
- const Vector3 &v0 = t0.value.loc;
- const Vector3 &v1 = t1.value.loc;
- const Vector3 &v2 = t2.value.loc;
+ } else {
+ Vector3 pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false;
+ }
- if (v0.is_equal_approx(v2)) {
- //0 and 2 are close, let's see if 1 is close
- if (!v0.is_equal_approx(v1)) {
- //not close, not optimizable
- return false;
- }
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
- } else {
- Vector3 pd = (v2 - v0);
- real_t d0 = pd.dot(v0);
- real_t d1 = pd.dot(v1);
- real_t d2 = pd.dot(v2);
- if (d1 < d0 || d1 > d2) {
- return false;
- }
+ if (d > pd.length() * p_allowed_linear_err) {
+ return false; //beyond allowed error for collinearity
+ }
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) {
+ return false;
+ }
+ }
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
- }
+ return true;
+}
- if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) {
- return false;
- }
+bool Animation::_rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) {
+ const Quaternion &q0 = t0.value;
+ const Quaternion &q1 = t1.value;
+ const Quaternion &q2 = t2.value;
- t[0] = (d1 - d0) / (d2 - d0);
+ //localize both to rotation from q0
+
+ if (q0.is_equal_approx(q2)) {
+ if (!q0.is_equal_approx(q1)) {
+ return false;
}
- }
- { //rotation
+ } else {
+ Quaternion r02 = (q0.inverse() * q2).normalized();
+ Quaternion r01 = (q0.inverse() * q1).normalized();
- const Quaternion &q0 = t0.value.rot;
- const Quaternion &q1 = t1.value.rot;
- const Quaternion &q2 = t2.value.rot;
+ Vector3 v02, v01;
+ real_t a02, a01;
- //localize both to rotation from q0
+ r02.get_axis_angle(v02, a02);
+ r01.get_axis_angle(v01, a01);
- if (q0.is_equal_approx(q2)) {
- if (!q0.is_equal_approx(q1)) {
- return false;
- }
+ if (Math::abs(a02) > p_max_optimizable_angle) {
+ return false;
+ }
- } else {
- Quaternion r02 = (q0.inverse() * q2).normalized();
- Quaternion r01 = (q0.inverse() * q1).normalized();
+ if (v01.dot(v02) < 0) {
+ //make sure both rotations go the same way to compare
+ v02 = -v02;
+ a02 = -a02;
+ }
- Vector3 v02, v01;
- real_t a02, a01;
+ real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI;
+ if (err_01 > p_allowed_angular_error) {
+ //not rotating in the same axis
+ return false;
+ }
- r02.get_axis_angle(v02, a02);
- r01.get_axis_angle(v01, a01);
+ if (a01 * a02 < 0) {
+ //not rotating in the same direction
+ return false;
+ }
- if (Math::abs(a02) > p_max_optimizable_angle) {
- return false;
- }
+ real_t tr = a01 / a02;
+ if (tr < 0 || tr > 1) {
+ return false; //rotating too much or too less
+ }
+ }
- if (v01.dot(v02) < 0) {
- //make sure both rotations go the same way to compare
- v02 = -v02;
- a02 = -a02;
- }
+ return true;
+}
- real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI;
- if (err_01 > p_alowed_angular_err) {
- //not rotating in the same axis
- return false;
- }
+bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error) {
+ const Vector3 &v0 = t0.value;
+ const Vector3 &v1 = t1.value;
+ const Vector3 &v2 = t2.value;
- if (a01 * a02 < 0) {
- //not rotating in the same direction
- return false;
- }
+ if (v0.is_equal_approx(v2)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!v0.is_equal_approx(v1)) {
+ //not close, not optimizable
+ return false;
+ }
- real_t tr = a01 / a02;
- if (tr < 0 || tr > 1) {
- return false; //rotating too much or too less
- }
+ } else {
+ Vector3 pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false; //beyond segment range
+ }
+
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
- t[1] = tr;
+ if (d > pd.length() * p_allowed_linear_error) {
+ return false; //beyond allowed error for colinearity
}
}
- { //scale
+ return true;
+}
- const Vector3 &v0 = t0.value.scale;
- const Vector3 &v1 = t1.value.scale;
- const Vector3 &v2 = t2.value.scale;
+bool Animation::_blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error) {
+ float v0 = t0.value;
+ float v1 = t1.value;
+ float v2 = t2.value;
- if (v0.is_equal_approx(v2)) {
- //0 and 2 are close, let's see if 1 is close
- if (!v0.is_equal_approx(v1)) {
- //not close, not optimizable
- return false;
- }
-
- } else {
- Vector3 pd = (v2 - v0);
- real_t d0 = pd.dot(v0);
- real_t d1 = pd.dot(v1);
- real_t d2 = pd.dot(v2);
- if (d1 < d0 || d1 > d2) {
- return false; //beyond segment range
- }
+ if (Math::is_equal_approx(v1, v2, p_allowed_unit_error)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!Math::is_equal_approx(v0, v1, p_allowed_unit_error)) {
+ //not close, not optimizable
+ return false;
+ }
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ } else {
+ /*
+ TODO eventually discuss a way to optimize these better.
+ float pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false; //beyond segment range
+ }
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
- }
+ float s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
- t[2] = (d1 - d0) / (d2 - d0);
+ if (d > pd.length() * p_allowed_linear_error) {
+ return false; //beyond allowed error for colinearity
}
+*/
}
- bool erase = false;
- if (t[0] == -1 && t[1] == -1 && t[2] == -1) {
- erase = true;
- } else {
- erase = true;
- real_t lt = -1.0;
- for (int j = 0; j < 3; j++) {
- //search for t on first, one must be it
- if (t[j] != -1) {
- lt = t[j]; //official t
- //validate rest
- for (int k = j + 1; k < 3; k++) {
- if (t[k] == -1) {
- continue;
- }
+ return true;
+}
- if (Math::abs(lt - t[k]) > p_alowed_linear_err) {
- erase = false;
- break;
- }
- }
- break;
- }
+void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D);
+ PositionTrack *tt = static_cast<PositionTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Vector3> first_erased;
+
+ Vector3 norm;
+
+ for (int i = 1; i < tt->positions.size() - 1; i++) {
+ TKey<Vector3> &t0 = tt->positions.write[i - 1];
+ TKey<Vector3> &t1 = tt->positions.write[i];
+ TKey<Vector3> &t2 = tt->positions.write[i + 1];
+
+ bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm);
+ if (erase && !prev_erased) {
+ norm = (t2.value - t1.value).normalized();
}
- ERR_FAIL_COND_V(lt == -1, false);
+ if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) {
+ //avoid error to go beyond first erased key
+ erase = false;
+ }
if (erase) {
- if (Math::abs(lt - c) > p_alowed_linear_err) {
- //todo, evaluate changing the transition if this fails?
- //this could be done as a second pass and would be
- //able to optimize more
- erase = false;
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
+
+ tt->positions.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
+ norm = Vector3();
}
}
+}
+
+void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D);
+ RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Quaternion> first_erased;
+
+ for (int i = 1; i < tt->rotations.size() - 1; i++) {
+ TKey<Quaternion> &t0 = tt->rotations.write[i - 1];
+ TKey<Quaternion> &t1 = tt->rotations.write[i];
+ TKey<Quaternion> &t2 = tt->rotations.write[i + 1];
- return erase;
+ bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle);
+
+ if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) {
+ //avoid error to go beyond first erased key
+ erase = false;
+ }
+
+ if (erase) {
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
+ }
+
+ tt->rotations.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
+ }
+ }
}
-void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
+void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) {
ERR_FAIL_INDEX(p_idx, tracks.size());
- ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D);
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]);
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D);
+ ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]);
bool prev_erased = false;
- TKey<TransformKey> first_erased;
+ TKey<Vector3> first_erased;
- Vector3 norm;
+ for (int i = 1; i < tt->scales.size() - 1; i++) {
+ TKey<Vector3> &t0 = tt->scales.write[i - 1];
+ TKey<Vector3> &t1 = tt->scales.write[i];
+ TKey<Vector3> &t2 = tt->scales.write[i + 1];
- for (int i = 1; i < tt->transforms.size() - 1; i++) {
- TKey<TransformKey> &t0 = tt->transforms.write[i - 1];
- TKey<TransformKey> &t1 = tt->transforms.write[i];
- TKey<TransformKey> &t2 = tt->transforms.write[i + 1];
+ bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
- bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm);
- if (erase && !prev_erased) {
- norm = (t2.value.loc - t1.value.loc).normalized();
+ if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) {
+ //avoid error to go beyond first erased key
+ erase = false;
}
- if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) {
+ if (erase) {
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
+ }
+
+ tt->scales.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
+ }
+ }
+}
+
+void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_err) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_BLEND_SHAPE);
+ BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<float> first_erased;
+ first_erased.value = 0.0;
+
+ for (int i = 1; i < tt->blend_shapes.size() - 1; i++) {
+ TKey<float> &t0 = tt->blend_shapes.write[i - 1];
+ TKey<float> &t1 = tt->blend_shapes.write[i];
+ TKey<float> &t2 = tt->blend_shapes.write[i + 1];
+
+ bool erase = _blend_shape_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
+
+ if (prev_erased && !_blend_shape_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) {
//avoid error to go beyond first erased key
erase = false;
}
@@ -2906,20 +3436,25 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err
prev_erased = true;
}
- tt->transforms.remove(i);
+ tt->blend_shapes.remove(i);
i--;
} else {
prev_erased = false;
- norm = Vector3();
}
}
}
void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
for (int i = 0; i < tracks.size(); i++) {
- if (tracks[i]->type == TYPE_TRANSFORM3D) {
- _transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle);
+ if (tracks[i]->type == TYPE_POSITION_3D) {
+ _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err);
+ } else if (tracks[i]->type == TYPE_ROTATION_3D) {
+ _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle);
+ } else if (tracks[i]->type == TYPE_SCALE_3D) {
+ _scale_track_optimize(i, p_allowed_linear_err);
+ } else if (tracks[i]->type == TYPE_BLEND_SHAPE) {
+ _blend_shape_track_optimize(i, p_allowed_linear_err);
}
}
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 9a410bd566..4ee0741d87 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -42,7 +42,10 @@ class Animation : public Resource {
public:
enum TrackType {
TYPE_VALUE, ///< Set a value in a property, can be interpolated.
- TYPE_TRANSFORM3D, ///< Transform a node or a bone.
+ TYPE_POSITION_3D, ///< Position 3D track
+ TYPE_ROTATION_3D, ///< Rotation 3D track
+ TYPE_SCALE_3D, ///< Scale 3D track
+ TYPE_BLEND_SHAPE, ///< Blend Shape track
TYPE_METHOD, ///< Call any method on a specific node.
TYPE_BEZIER, ///< Bezier curve
TYPE_AUDIO,
@@ -86,21 +89,39 @@ private:
T value;
};
- struct TransformKey {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
+ const int32_t POSITION_TRACK_SIZE = 5;
+ const int32_t ROTATION_TRACK_SIZE = 6;
+ const int32_t SCALE_TRACK_SIZE = 5;
+ const int32_t BLEND_SHAPE_TRACK_SIZE = 3;
+
+ /* POSITION TRACK */
+
+ struct PositionTrack : public Track {
+ Vector<TKey<Vector3>> positions;
+
+ PositionTrack() { type = TYPE_POSITION_3D; }
};
- // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
- const int32_t TRANSFORM_TRACK_SIZE = 12;
+ /* ROTATION TRACK */
- /* TRANSFORM TRACK */
+ struct RotationTrack : public Track {
+ Vector<TKey<Quaternion>> rotations;
+
+ RotationTrack() { type = TYPE_ROTATION_3D; }
+ };
- struct TransformTrack : public Track {
- Vector<TKey<TransformKey>> transforms;
+ /* SCALE TRACK */
- TransformTrack() { type = TYPE_TRANSFORM3D; }
+ struct ScaleTrack : public Track {
+ Vector<TKey<Vector3>> scales;
+ ScaleTrack() { type = TYPE_SCALE_3D; }
+ };
+
+ /* BLEND SHAPE TRACK */
+
+ struct BlendShapeTrack : public Track {
+ Vector<TKey<float>> blend_shapes;
+ BlendShapeTrack() { type = TYPE_BLEND_SHAPE; }
};
/* PROPERTY VALUE TRACK */
@@ -186,14 +207,11 @@ private:
template <class K>
inline int _find(const Vector<K> &p_keys, double p_time) const;
- _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
-
_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
_FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
_FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
- _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
@@ -214,18 +232,6 @@ private:
// bind helpers
private:
- Array _transform_track_interpolate(int p_track, double p_time) const {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- transform_track_interpolate(p_track, p_time, &loc, &rot, &scale);
- Array ret;
- ret.push_back(loc);
- ret.push_back(rot);
- ret.push_back(scale);
- return ret;
- }
-
Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
@@ -247,8 +253,15 @@ private:
return idxr;
}
- bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
- void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
+ bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm);
+ bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle);
+ bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error);
+ bool _blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error);
+
+ void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err);
+ void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle);
+ void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err);
+ void _blend_shape_track_optimize(int p_idx, real_t p_allowed_unit_error);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -294,8 +307,22 @@ public:
double track_get_key_time(int p_track, int p_key_idx) const;
real_t track_get_key_transition(int p_track, int p_key_idx) const;
- int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
- Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
+ int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
+ Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
+ Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
+ int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
+ Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
+ Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
+
+ int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
+ Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
+ Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
+ int blend_shape_track_insert_key(int p_track, double p_time, float p_blend);
+ Error blend_shape_track_get_key(int p_track, int p_key, float *r_blend) const;
+ Error blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const;
+
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
@@ -324,8 +351,6 @@ public:
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
- Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
-
Variant value_track_interpolate(int p_track, double p_time) const;
void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 2ab9b7b5a4..d018103e64 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -480,6 +480,10 @@ float AudioStreamSample::get_length() const {
return float(len) / mix_rate;
}
+bool AudioStreamSample::is_monophonic() const {
+ return false;
+}
+
void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
AudioServer::get_singleton()->lock();
if (data) {
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 8bf3d29123..24198e3c98 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -136,6 +136,8 @@ public:
virtual float get_length() const override; //if supported, otherwise return 0
+ virtual bool is_monophonic() const override;
+
void set_data(const Vector<uint8_t> &p_data);
Vector<uint8_t> get_data() const;
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index de557494c3..49ed9dcb82 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -48,7 +48,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol
img->convert(Image::FORMAT_LA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8);
- create(Size2(img->get_width(), img->get_height()));
+ create(img->get_size());
const uint8_t *r = img->get_data().ptr();
uint8_t *w = bitmask.ptrw();
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index 7501efea9e..b291724c4c 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -135,7 +135,7 @@ void CanvasItemMaterial::_update_shader() {
code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
code += " }";
code += " UV /= vec2(h_frames, v_frames);\n";
- code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n";
code += "}\n";
}
@@ -161,7 +161,7 @@ void CanvasItemMaterial::flush_changes() {
void CanvasItemMaterial::_queue_shader_change() {
MutexLock lock(material_mutex);
- if (!element.in_list()) {
+ if (is_initialized && !element.in_list()) {
dirty_materials->add(&element);
}
}
@@ -287,6 +287,7 @@ CanvasItemMaterial::CanvasItemMaterial() :
set_particles_anim_loop(false);
current_key.invalid_key = 1;
+ is_initialized = true;
_queue_shader_change();
}
diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h
index 0a813e0ae5..37cd4de136 100644
--- a/scene/resources/canvas_item_material.h
+++ b/scene/resources/canvas_item_material.h
@@ -102,6 +102,7 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
+ bool is_initialized = false;
BlendMode blend_mode = BLEND_MODE_MIX;
LightMode light_mode = LIGHT_MODE_NORMAL;
bool particles_animation = false;
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index a364a27e80..9dc76dcf44 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -966,9 +966,9 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) cons
int pidx = 0;
for (int i = 0; i < points.size() - 1; i++) {
- for (Map<float, Vector2>::Element *E = midpoints[i].front(); E; E = E->next()) {
+ for (const KeyValue<float, Vector2> &E : midpoints[i]) {
pidx++;
- bpw[pidx] = E->get();
+ bpw[pidx] = E.value;
}
pidx++;
@@ -1652,9 +1652,9 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) cons
int pidx = 0;
for (int i = 0; i < points.size() - 1; i++) {
- for (Map<float, Vector3>::Element *E = midpoints[i].front(); E; E = E->next()) {
+ for (const KeyValue<float, Vector3> &E : midpoints[i]) {
pidx++;
- bpw[pidx] = E->get();
+ bpw[pidx] = E.value;
}
pidx++;
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
index 0fb6bb2c62..3667ab7c14 100644
--- a/scene/resources/default_theme/SCsub
+++ b/scene/resources/default_theme/SCsub
@@ -2,8 +2,6 @@
Import("env")
-import os
-import os.path
from platform_methods import run_in_subprocess
import default_theme_builders
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 4845c556c6..9fdfd493c1 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -51,7 +51,7 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl
} else {
texture = Ref<ImageTexture>(memnew(ImageTexture));
Ref<Image> img = memnew(Image(p_src));
- const Size2 orig_size = Size2(img->get_width(), img->get_height());
+ const Size2 orig_size = img->get_size();
img->convert(Image::FORMAT_RGBA8);
img->resize(orig_size.x * scale, orig_size.y * scale);
@@ -97,7 +97,7 @@ template <class T>
static Ref<Texture2D> make_icon(T p_src) {
Ref<ImageTexture> texture(memnew(ImageTexture));
Ref<Image> img = memnew(Image(p_src));
- const Size2 orig_size = Size2(img->get_width(), img->get_height());
+ const Size2 orig_size = img->get_size();
img->convert(Image::FORMAT_RGBA8);
img->resize(orig_size.x * scale, orig_size.y * scale);
texture->create_from_image(img);
@@ -438,6 +438,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("caret_color", "TextEdit", control_font_color);
theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0));
theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
+ theme->set_color("search_result_color", "TextEdit", Color(0.3, 0.3, 0.3));
+ theme->set_color("search_result_border_color", "TextEdit", Color(0.3, 0.3, 0.3, 0.4));
theme->set_constant("line_spacing", "TextEdit", 4 * scale);
theme->set_constant("outline_size", "TextEdit", 0);
@@ -483,6 +485,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4));
theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15));
theme->set_color("line_length_guideline_color", "CodeEdit", Color(0.3, 0.5, 0.8, 0.1));
+ theme->set_color("search_result_color", "CodeEdit", Color(0.3, 0.3, 0.3));
+ theme->set_color("search_result_border_color", "CodeEdit", Color(0.3, 0.3, 0.3, 0.4));
theme->set_constant("completion_lines", "CodeEdit", 7);
theme->set_constant("completion_max_width", "CodeEdit", 50);
@@ -502,8 +506,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("increment", "HScrollBar", empty_icon);
theme->set_icon("increment_highlight", "HScrollBar", empty_icon);
+ theme->set_icon("increment_pressed", "HScrollBar", empty_icon);
theme->set_icon("decrement", "HScrollBar", empty_icon);
theme->set_icon("decrement_highlight", "HScrollBar", empty_icon);
+ theme->set_icon("decrement_pressed", "HScrollBar", empty_icon);
// VScrollBar
@@ -515,8 +521,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("increment", "VScrollBar", empty_icon);
theme->set_icon("increment_highlight", "VScrollBar", empty_icon);
+ theme->set_icon("increment_pressed", "VScrollBar", empty_icon);
theme->set_icon("decrement", "VScrollBar", empty_icon);
theme->set_icon("decrement_highlight", "VScrollBar", empty_icon);
+ theme->set_icon("decrement_pressed", "VScrollBar", empty_icon);
// HSlider
@@ -777,30 +785,30 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("icon_separation", "TabContainer", 4 * scale);
theme->set_constant("outline_size", "TabContainer", 0);
- // Tabs
+ // TabBar
- theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
- theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
- theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("button_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("button", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4));
+ theme->set_stylebox("tab_selected", "TabBar", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
+ theme->set_stylebox("tab_unselected", "TabBar", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
+ theme->set_stylebox("tab_disabled", "TabBar", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
+ theme->set_stylebox("close_bg_pressed", "TabBar", make_stylebox(button_pressed_png, 4, 4, 4, 4));
+ theme->set_stylebox("close_bg_highlight", "TabBar", make_stylebox(button_normal_png, 4, 4, 4, 4));
- theme->set_icon("increment", "Tabs", make_icon(scroll_button_right_png));
- theme->set_icon("increment_highlight", "Tabs", make_icon(scroll_button_right_hl_png));
- theme->set_icon("decrement", "Tabs", make_icon(scroll_button_left_png));
- theme->set_icon("decrement_highlight", "Tabs", make_icon(scroll_button_left_hl_png));
- theme->set_icon("close", "Tabs", make_icon(tab_close_png));
+ theme->set_icon("increment", "TabBar", make_icon(scroll_button_right_png));
+ theme->set_icon("increment_highlight", "TabBar", make_icon(scroll_button_right_hl_png));
+ theme->set_icon("decrement", "TabBar", make_icon(scroll_button_left_png));
+ theme->set_icon("decrement_highlight", "TabBar", make_icon(scroll_button_left_hl_png));
+ theme->set_icon("close", "TabBar", make_icon(tab_close_png));
- theme->set_font("font", "Tabs", Ref<Font>());
- theme->set_font_size("font_size", "Tabs", -1);
+ theme->set_font("font", "TabBar", Ref<Font>());
+ theme->set_font_size("font_size", "TabBar", -1);
- theme->set_color("font_selected_color", "Tabs", control_font_hover_color);
- theme->set_color("font_unselected_color", "Tabs", control_font_low_color);
- theme->set_color("font_disabled_color", "Tabs", control_font_disabled_color);
- theme->set_color("font_outline_color", "Tabs", Color(1, 1, 1));
+ theme->set_color("font_selected_color", "TabBar", control_font_hover_color);
+ theme->set_color("font_unselected_color", "TabBar", control_font_low_color);
+ theme->set_color("font_disabled_color", "TabBar", control_font_disabled_color);
+ theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1));
- theme->set_constant("hseparation", "Tabs", 4 * scale);
- theme->set_constant("outline_size", "Tabs", 0);
+ theme->set_constant("hseparation", "TabBar", 4 * scale);
+ theme->set_constant("outline_size", "TabBar", 0);
// Separators
@@ -1028,9 +1036,16 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
}
Ref<Font> large_font = default_font;
- fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0);
+
+ float default_scale = 1.0;
+ if (p_hidpi) {
+ default_scale = 2.0;
+ }
+
+ fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale);
Theme::set_default(t);
+ Theme::set_default_base_scale(default_scale);
Theme::set_default_icon(default_icon);
Theme::set_default_style(default_style);
Theme::set_default_font(default_font);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index cab6c0378a..be0d3a140e 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -173,23 +173,13 @@ Environment::ReflectionSource Environment::get_reflection_source() const {
return reflection_source;
}
-void Environment::set_ao_color(const Color &p_color) {
- ao_color = p_color;
- _update_ambient_light();
-}
-
-Color Environment::get_ao_color() const {
- return ao_color;
-}
-
void Environment::_update_ambient_light() {
RS::get_singleton()->environment_set_ambient_light(
environment,
ambient_color,
RS::EnvironmentAmbientSource(ambient_source),
ambient_energy,
- ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source),
- ao_color);
+ ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source));
}
// Tonemap
@@ -302,7 +292,7 @@ int Environment::get_ssr_max_steps() const {
}
void Environment::set_ssr_fade_in(float p_fade_in) {
- ssr_fade_in = p_fade_in;
+ ssr_fade_in = MAX(p_fade_in, 0.0f);
_update_ssr();
}
@@ -311,7 +301,7 @@ float Environment::get_ssr_fade_in() const {
}
void Environment::set_ssr_fade_out(float p_fade_out) {
- ssr_fade_out = p_fade_out;
+ ssr_fade_out = MAX(p_fade_out, 0.0f);
_update_ssr();
}
@@ -1092,15 +1082,12 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution);
ClassDB::bind_method(D_METHOD("set_reflection_source", "source"), &Environment::set_reflection_source);
ClassDB::bind_method(D_METHOD("get_reflection_source"), &Environment::get_reflection_source);
- ClassDB::bind_method(D_METHOD("set_ao_color", "color"), &Environment::set_ao_color);
- ClassDB::bind_method(D_METHOD("get_ao_color"), &Environment::get_ao_color);
ADD_GROUP("Ambient Light", "ambient_light_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Color,Sky"), "set_ambient_source", "get_ambient_source");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_sky_contribution", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ambient_light_sky_contribution", "get_ambient_light_sky_contribution");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_occlusion_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ao_color", "get_ao_color");
ADD_GROUP("Reflected Light", "reflected_light_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "reflected_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Sky"), "set_reflection_source", "get_reflection_source");
@@ -1304,7 +1291,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density");
ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled);
ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled);
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 0df2c3cc27..46842754f4 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -116,7 +116,6 @@ private:
float ambient_energy = 1.0;
float ambient_sky_contribution = 1.0;
ReflectionSource reflection_source = REFLECTION_SOURCE_BG;
- Color ao_color;
void _update_ambient_light();
// Tonemap
@@ -250,8 +249,6 @@ public:
float get_ambient_light_sky_contribution() const;
void set_reflection_source(ReflectionSource p_source);
ReflectionSource get_reflection_source() const;
- void set_ao_color(const Color &p_color);
- Color get_ao_color() const;
// Tonemap
void set_tonemapper(ToneMapper p_tone_mapper);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 7af8e900fd..04e2b0dc70 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -116,8 +116,8 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale);
ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale);
- ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing"), &FontData::set_spacing);
- ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size"), &FontData::get_spacing);
+ ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing);
ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count);
ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures);
@@ -175,7 +175,7 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
- ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index);
+ ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index);
ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
@@ -502,6 +502,11 @@ void FontData::set_data(const PackedByteArray &p_data) {
}
PackedByteArray FontData::get_data() const {
+ if (unlikely((size_t)data.size() != data_size)) {
+ PackedByteArray *data_w = const_cast<PackedByteArray *>(&data);
+ data_w->resize(data_size);
+ memcpy(data_w->ptrw(), data_ptr, data_size);
+ }
return data;
}
@@ -689,227 +694,272 @@ void FontData::remove_cache(int p_cache_index) {
}
Array FontData::get_size_cache_list(int p_cache_index) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Array());
_ensure_rid(p_cache_index);
return TS->font_get_size_cache_list(cache[p_cache_index]);
}
void FontData::clear_size_cache(int p_cache_index) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_clear_size_cache(cache[p_cache_index]);
}
void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_remove_size_cache(cache[p_cache_index], p_size);
}
void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates);
emit_changed();
}
Dictionary FontData::get_variation_coordinates(int p_cache_index) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Dictionary());
_ensure_rid(p_cache_index);
return TS->font_get_variation_coordinates(cache[p_cache_index]);
}
void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent);
}
real_t FontData::get_ascent(int p_cache_index, int p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0.f);
_ensure_rid(p_cache_index);
return TS->font_get_ascent(cache[p_cache_index], p_size);
}
void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_descent(cache[p_cache_index], p_size, p_descent);
}
real_t FontData::get_descent(int p_cache_index, int p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0.f);
_ensure_rid(p_cache_index);
return TS->font_get_descent(cache[p_cache_index], p_size);
}
void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position);
}
real_t FontData::get_underline_position(int p_cache_index, int p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0.f);
_ensure_rid(p_cache_index);
return TS->font_get_underline_position(cache[p_cache_index], p_size);
}
void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness);
}
real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0.f);
_ensure_rid(p_cache_index);
return TS->font_get_underline_thickness(cache[p_cache_index], p_size);
}
void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_scale(cache[p_cache_index], p_size, p_scale);
}
real_t FontData::get_scale(int p_cache_index, int p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0.f);
_ensure_rid(p_cache_index);
return TS->font_get_scale(cache[p_cache_index], p_size);
}
void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value);
}
int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0);
_ensure_rid(p_cache_index);
return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing);
}
int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0);
_ensure_rid(p_cache_index);
return TS->font_get_texture_count(cache[p_cache_index], p_size);
}
void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_clear_textures(cache[p_cache_index], p_size);
}
void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index);
}
void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image);
}
Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Ref<Image>());
_ensure_rid(p_cache_index);
return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index);
}
void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset);
}
PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array());
_ensure_rid(p_cache_index);
return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index);
}
Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Array());
_ensure_rid(p_cache_index);
return TS->font_get_glyph_list(cache[p_cache_index], p_size);
}
void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_clear_glyphs(cache[p_cache_index], p_size);
}
void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph);
}
void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance);
}
Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Vector2());
_ensure_rid(p_cache_index);
return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph);
}
void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset);
}
Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Vector2());
_ensure_rid(p_cache_index);
return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph);
}
void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size);
}
Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Vector2());
_ensure_rid(p_cache_index);
return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph);
}
void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect);
}
Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Rect2());
_ensure_rid(p_cache_index);
return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph);
}
void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx);
}
int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, 0);
_ensure_rid(p_cache_index);
return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph);
}
Array FontData::get_kerning_list(int p_cache_index, int p_size) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Array());
_ensure_rid(p_cache_index);
return TS->font_get_kerning_list(cache[p_cache_index], p_size);
}
void FontData::clear_kerning_map(int p_cache_index, int p_size) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_clear_kerning_map(cache[p_cache_index], p_size);
}
void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair);
}
void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning);
}
Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, Vector2());
_ensure_rid(p_cache_index);
return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair);
}
void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end);
}
void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) {
+ ERR_FAIL_COND(p_cache_index < 0);
_ensure_rid(p_cache_index);
TS->font_render_glyph(cache[p_cache_index], p_size, p_index);
}
RID FontData::get_cache_rid(int p_cache_index) const {
+ ERR_FAIL_COND_V(p_cache_index < 0, RID());
_ensure_rid(p_cache_index);
return cache[p_cache_index];
}
@@ -1008,10 +1058,8 @@ void Font::_data_changed() {
void Font::_ensure_rid(int p_index) const {
// Find or create cache record.
- for (int i = 0; i < rids.size(); i++) {
- if (!rids[i].is_valid() && data[i].is_valid()) {
- rids.write[i] = data[i]->find_cache(variation_coordinates);
- }
+ if (!rids[p_index].is_valid() && data[p_index].is_valid()) {
+ rids.write[p_index] = data[p_index]->find_cache(variation_coordinates);
}
}
@@ -1171,6 +1219,14 @@ void Font::add_data(const Ref<FontData> &p_data) {
if (data[data.size() - 1].is_valid()) {
data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ Dictionary data_var_list = p_data->get_supported_variation_list();
+ for (int j = 0; j < data_var_list.size(); j++) {
+ int32_t tag = data_var_list.get_key_at_index(j);
+ Vector3i value = data_var_list.get_value_at_index(j);
+ if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) {
+ variation_coordinates[TS->tag_to_name(tag)] = value.z;
+ }
+ }
}
cache.clear();
@@ -1190,6 +1246,14 @@ void Font::set_data(int p_idx, const Ref<FontData> &p_data) {
data.write[p_idx] = p_data;
rids.write[p_idx] = RID();
+ Dictionary data_var_list = p_data->get_supported_variation_list();
+ for (int j = 0; j < data_var_list.size(); j++) {
+ int32_t tag = data_var_list.get_key_at_index(j);
+ Vector3i value = data_var_list.get_value_at_index(j);
+ if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) {
+ variation_coordinates[TS->tag_to_name(tag)] = value.z;
+ }
+ }
if (data[p_idx].is_valid()) {
data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED);
@@ -1340,7 +1404,7 @@ real_t Font::get_underline_thickness(int p_size) const {
return ret;
}
-Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint8_t p_flags) const {
+Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
int size = (p_size <= 0) ? base_size : p_size;
@@ -1367,7 +1431,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
return buffer->get_size();
}
-Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint8_t p_flags) const {
+Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
int size = (p_size <= 0) ? base_size : p_size;
@@ -1406,7 +1470,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
return ret;
}
-void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
int size = (p_size <= 0) ? base_size : p_size;
@@ -1448,7 +1512,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
buffer->draw(p_canvas_item, ofs, p_modulate);
}
-void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
int size = (p_size <= 0) ? base_size : p_size;
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 9a34edce64..e65fdb0751 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -266,11 +266,11 @@ public:
virtual real_t get_underline_thickness(int p_size = -1) const;
// Drawing string.
- virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
+ virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
// Helper functions.
virtual bool has_char(char32_t p_char) const;
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
index 05d1a7bf94..fe7124de9e 100644
--- a/scene/resources/immediate_mesh.cpp
+++ b/scene/resources/immediate_mesh.cpp
@@ -335,9 +335,6 @@ int ImmediateMesh::surface_get_array_len(int p_idx) const {
int ImmediateMesh::surface_get_array_index_len(int p_idx) const {
return 0;
}
-bool ImmediateMesh::surface_is_softbody_friendly(int p_idx) const {
- return false;
-}
Array ImmediateMesh::surface_get_arrays(int p_surface) const {
ERR_FAIL_INDEX_V(p_surface, int(surfaces.size()), Array());
return RS::get_singleton()->mesh_surface_get_arrays(mesh, p_surface);
diff --git a/scene/resources/immediate_mesh.h b/scene/resources/immediate_mesh.h
index 7010d40719..6673ee6f3d 100644
--- a/scene/resources/immediate_mesh.h
+++ b/scene/resources/immediate_mesh.h
@@ -94,7 +94,6 @@ public:
virtual int get_surface_count() const override;
virtual int surface_get_array_len(int p_idx) const override;
virtual int surface_get_array_index_len(int p_idx) const override;
- virtual bool surface_is_softbody_friendly(int p_idx) const override;
virtual Array surface_get_arrays(int p_surface) const override;
virtual Array surface_get_blend_shape_arrays(int p_surface) const override;
virtual Dictionary surface_get_lods(int p_surface) const override;
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
new file mode 100644
index 0000000000..076b8312b6
--- /dev/null
+++ b/scene/resources/importer_mesh.cpp
@@ -0,0 +1,1247 @@
+/*************************************************************************/
+/* importer_mesh.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 "importer_mesh.h"
+
+#include "core/math/random_pcg.h"
+#include "core/math/static_raycaster.h"
+#include "scene/resources/surface_tool.h"
+
+#include <cstdint>
+
+void ImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
+ _split_normals(arrays, p_indices, p_normals);
+
+ for (BlendShape &blend_shape : blend_shape_data) {
+ _split_normals(blend_shape.arrays, p_indices, p_normals);
+ }
+}
+
+void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
+ ERR_FAIL_COND(r_arrays.size() != RS::ARRAY_MAX);
+
+ const PackedVector3Array &vertices = r_arrays[RS::ARRAY_VERTEX];
+ int current_vertex_count = vertices.size();
+ int new_vertex_count = p_indices.size();
+ int final_vertex_count = current_vertex_count + new_vertex_count;
+ const int *indices_ptr = p_indices.ptr();
+
+ for (int i = 0; i < r_arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ if (r_arrays[i].get_type() == Variant::NIL) {
+ continue;
+ }
+
+ switch (r_arrays[i].get_type()) {
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ PackedVector3Array data = r_arrays[i];
+ data.resize(final_vertex_count);
+ Vector3 *data_ptr = data.ptrw();
+ if (i == RS::ARRAY_NORMAL) {
+ const Vector3 *normals_ptr = p_normals.ptr();
+ memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count);
+ } else {
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ PackedVector2Array data = r_arrays[i];
+ data.resize(final_vertex_count);
+ Vector2 *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ PackedFloat32Array data = r_arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ float *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements);
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ PackedInt32Array data = r_arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ int32_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements);
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ PackedByteArray data = r_arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ uint8_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements);
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ PackedColorArray data = r_arrays[i];
+ data.resize(final_vertex_count);
+ Color *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ r_arrays[i] = data;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Unhandled array type.");
+ } break;
+ }
+ }
+}
+
+void ImporterMesh::add_blend_shape(const String &p_name) {
+ ERR_FAIL_COND(surfaces.size() > 0);
+ blend_shapes.push_back(p_name);
+}
+
+int ImporterMesh::get_blend_shape_count() const {
+ return blend_shapes.size();
+}
+
+String ImporterMesh::get_blend_shape_name(int p_blend_shape) const {
+ ERR_FAIL_INDEX_V(p_blend_shape, blend_shapes.size(), String());
+ return blend_shapes[p_blend_shape];
+}
+
+void ImporterMesh::set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode) {
+ blend_shape_mode = p_blend_shape_mode;
+}
+
+Mesh::BlendShapeMode ImporterMesh::get_blend_shape_mode() const {
+ return blend_shape_mode;
+}
+
+void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) {
+ ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size());
+ ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX);
+ Surface s;
+ s.primitive = p_primitive;
+ s.arrays = p_arrays;
+ s.name = p_name;
+ s.flags = p_flags;
+
+ Vector<Vector3> vertex_array = p_arrays[Mesh::ARRAY_VERTEX];
+ int vertex_count = vertex_array.size();
+ ERR_FAIL_COND(vertex_count == 0);
+
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ Array bsdata = p_blend_shapes[i];
+ ERR_FAIL_COND(bsdata.size() != Mesh::ARRAY_MAX);
+ Vector<Vector3> vertex_data = bsdata[Mesh::ARRAY_VERTEX];
+ ERR_FAIL_COND(vertex_data.size() != vertex_count);
+ Surface::BlendShape bs;
+ bs.arrays = bsdata;
+ s.blend_shape_data.push_back(bs);
+ }
+
+ List<Variant> lods;
+ p_lods.get_key_list(&lods);
+ for (const Variant &E : lods) {
+ ERR_CONTINUE(!E.is_num());
+ Surface::LOD lod;
+ lod.distance = E;
+ lod.indices = p_lods[E];
+ ERR_CONTINUE(lod.indices.size() == 0);
+ s.lods.push_back(lod);
+ }
+
+ s.material = p_material;
+
+ surfaces.push_back(s);
+ mesh.unref();
+}
+
+int ImporterMesh::get_surface_count() const {
+ return surfaces.size();
+}
+
+Mesh::PrimitiveType ImporterMesh::get_surface_primitive_type(int p_surface) {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Mesh::PRIMITIVE_MAX);
+ return surfaces[p_surface].primitive;
+}
+Array ImporterMesh::get_surface_arrays(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array());
+ return surfaces[p_surface].arrays;
+}
+String ImporterMesh::get_surface_name(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), String());
+ return surfaces[p_surface].name;
+}
+void ImporterMesh::set_surface_name(int p_surface, const String &p_name) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ surfaces.write[p_surface].name = p_name;
+ mesh.unref();
+}
+
+Array ImporterMesh::get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array());
+ ERR_FAIL_INDEX_V(p_blend_shape, surfaces[p_surface].blend_shape_data.size(), Array());
+ return surfaces[p_surface].blend_shape_data[p_blend_shape].arrays;
+}
+int ImporterMesh::get_surface_lod_count(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ return surfaces[p_surface].lods.size();
+}
+Vector<int> ImporterMesh::get_surface_lod_indices(int p_surface, int p_lod) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Vector<int>());
+ ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), Vector<int>());
+
+ return surfaces[p_surface].lods[p_lod].indices;
+}
+
+float ImporterMesh::get_surface_lod_size(int p_surface, int p_lod) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), 0);
+ return surfaces[p_surface].lods[p_lod].distance;
+}
+
+uint32_t ImporterMesh::get_surface_format(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ return surfaces[p_surface].flags;
+}
+
+Ref<Material> ImporterMesh::get_surface_material(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Ref<Material>());
+ return surfaces[p_surface].material;
+}
+
+void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ surfaces.write[p_surface].material = p_material;
+ mesh.unref();
+}
+
+void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) {
+ if (!SurfaceTool::simplify_scale_func) {
+ return;
+ }
+ if (!SurfaceTool::simplify_with_attrib_func) {
+ return;
+ }
+ if (!SurfaceTool::optimize_vertex_cache_func) {
+ return;
+ }
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
+ continue;
+ }
+
+ surfaces.write[i].lods.clear();
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
+ Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV];
+
+ unsigned int index_count = indices.size();
+ unsigned int vertex_count = vertices.size();
+
+ if (index_count == 0) {
+ continue; //no lods if no indices
+ }
+
+ const Vector3 *vertices_ptr = vertices.ptr();
+ const int *indices_ptr = indices.ptr();
+
+ if (normals.is_empty()) {
+ normals.resize(vertices.size());
+ Vector3 *n_ptr = normals.ptrw();
+ for (unsigned int j = 0; j < index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]];
+ Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized();
+ n_ptr[j + 0] = n;
+ n_ptr[j + 1] = n;
+ n_ptr[j + 2] = n;
+ }
+ }
+
+ float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle));
+ float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f)));
+ float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle));
+ const Vector3 *normals_ptr = normals.ptr();
+
+ Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
+
+ LocalVector<int> vertex_remap;
+ LocalVector<int> vertex_inverse_remap;
+ LocalVector<Vector3> merged_vertices;
+ LocalVector<Vector3> merged_normals;
+ LocalVector<int> merged_normals_counts;
+ const Vector2 *uvs_ptr = uvs.ptr();
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const Vector3 &v = vertices_ptr[j];
+ const Vector3 &n = normals_ptr[j];
+
+ Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ const LocalVector<Pair<int, int>> &close_verts = E->get();
+
+ bool found = false;
+ for (unsigned int k = 0; k < close_verts.size(); k++) {
+ const Pair<int, int> &idx = close_verts[k];
+
+ // TODO check more attributes?
+ if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) {
+ vertex_remap.push_back(idx.first);
+ merged_normals[idx.first] += normals[idx.second];
+ merged_normals_counts[idx.first]++;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ int vcount = merged_vertices.size();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ } else {
+ int vcount = merged_vertices.size();
+ unique_vertices[v] = LocalVector<Pair<int, int>>();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ }
+
+ LocalVector<int> merged_indices;
+ merged_indices.resize(index_count);
+ for (unsigned int j = 0; j < index_count; j++) {
+ merged_indices[j] = vertex_remap[indices[j]];
+ }
+
+ unsigned int merged_vertex_count = merged_vertices.size();
+ const Vector3 *merged_vertices_ptr = merged_vertices.ptr();
+ const int32_t *merged_indices_ptr = merged_indices.ptr();
+
+ {
+ const int *counts_ptr = merged_normals_counts.ptr();
+ Vector3 *merged_normals_ptrw = merged_normals.ptr();
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ merged_normals_ptrw[j] /= counts_ptr[j];
+ }
+ }
+
+ LocalVector<float> normal_weights;
+ normal_weights.resize(merged_vertex_count);
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting
+ }
+
+ const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
+ float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3));
+ float mesh_error = 0.0f;
+
+ unsigned int index_target = 12; // Start with the smallest target, 4 triangles
+ unsigned int last_index_count = 0;
+
+ int split_vertex_count = vertex_count;
+ LocalVector<Vector3> split_vertex_normals;
+ LocalVector<int> split_vertex_indices;
+ split_vertex_normals.reserve(index_count / 3);
+ split_vertex_indices.reserve(index_count / 3);
+
+ RandomPCG pcg;
+ pcg.seed(123456789); // Keep seed constant across imports
+
+ Ref<StaticRaycaster> raycaster = StaticRaycaster::create();
+ if (raycaster.is_valid()) {
+ raycaster->add_mesh(vertices, indices, 0);
+ raycaster->commit();
+ }
+
+ while (index_target < index_count) {
+ PackedInt32Array new_indices;
+ new_indices.resize(index_count);
+
+ size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3);
+
+ if (new_index_count < last_index_count * 1.5f) {
+ index_target = index_target * 1.5f;
+ continue;
+ }
+
+ if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) {
+ break;
+ }
+
+ new_indices.resize(new_index_count);
+
+ LocalVector<LocalVector<int>> vertex_corners;
+ vertex_corners.resize(vertex_count);
+ {
+ int *ptrw = new_indices.ptrw();
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ const int &remapped = vertex_inverse_remap[ptrw[j]];
+ vertex_corners[remapped].push_back(j);
+ ptrw[j] = remapped;
+ }
+ }
+
+ if (raycaster.is_valid()) {
+ float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15));
+ const float ray_bias = 0.05;
+ float ray_length = ray_bias + mesh_error * scale * 3.0f;
+
+ Vector<StaticRaycaster::Ray> rays;
+ LocalVector<Vector2> ray_uvs;
+
+ int32_t *new_indices_ptr = new_indices.ptrw();
+
+ int current_ray_count = 0;
+ for (unsigned int j = 0; j < new_index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]];
+ Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1);
+ float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care
+
+ Vector3 dir = face_normal / face_area;
+ int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64);
+
+ rays.resize(current_ray_count + ray_count);
+ StaticRaycaster::Ray *rays_ptr = rays.ptrw();
+
+ ray_uvs.resize(current_ray_count + ray_count);
+ Vector2 *ray_uvs_ptr = ray_uvs.ptr();
+
+ for (int k = 0; k < ray_count; k++) {
+ float u = pcg.randf();
+ float v = pcg.randf();
+
+ if (u + v >= 1.0f) {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ }
+
+ u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge
+ v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third;
+ float w = 1.0f - u - v;
+
+ Vector3 org = v0 * w + v1 * u + v2 * v;
+ org -= dir * ray_bias;
+ rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length);
+ rays_ptr[current_ray_count + k].id = j / 3;
+ ray_uvs_ptr[current_ray_count + k] = Vector2(u, v);
+ }
+
+ current_ray_count += ray_count;
+ }
+
+ raycaster->intersect(rays);
+
+ LocalVector<Vector3> ray_normals;
+ LocalVector<float> ray_normal_weights;
+
+ ray_normals.resize(new_index_count);
+ ray_normal_weights.resize(new_index_count);
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ ray_normal_weights[j] = 0.0f;
+ }
+
+ const StaticRaycaster::Ray *rp = rays.ptr();
+ for (int j = 0; j < rays.size(); j++) {
+ if (rp[j].geomID != 0) { // Ray missed
+ continue;
+ }
+
+ if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face.
+ continue;
+ }
+
+ const float &u = rp[j].u;
+ const float &v = rp[j].v;
+ const float w = 1.0f - u - v;
+
+ const unsigned int &hit_tri_id = rp[j].primID;
+ const unsigned int &orig_tri_id = rp[j].id;
+
+ const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]];
+ const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]];
+ const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]];
+ Vector3 normal = n0 * w + n1 * u + n2 * v;
+
+ Vector2 orig_uv = ray_uvs[j];
+ float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
+ for (int k = 0; k < 3; k++) {
+ int idx = orig_tri_id * 3 + k;
+ float weight = orig_bary[k];
+ ray_normals[idx] += normal * weight;
+ ray_normal_weights[idx] += weight;
+ }
+ }
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess
+ ray_normals[j] = Vector3();
+ } else {
+ ray_normals[j] /= ray_normal_weights[j];
+ }
+ }
+
+ LocalVector<LocalVector<int>> normal_group_indices;
+ LocalVector<Vector3> normal_group_averages;
+ normal_group_indices.reserve(24);
+ normal_group_averages.reserve(24);
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const LocalVector<int> &corners = vertex_corners[j];
+ const Vector3 &vertex_normal = normals_ptr[j];
+
+ for (unsigned int k = 0; k < corners.size(); k++) {
+ const int &corner_idx = corners[k];
+ const Vector3 &ray_normal = ray_normals[corner_idx];
+
+ if (ray_normal.length_squared() < CMP_EPSILON2) {
+ continue;
+ }
+
+ bool found = false;
+ for (unsigned int l = 0; l < normal_group_indices.size(); l++) {
+ LocalVector<int> &group_indices = normal_group_indices[l];
+ Vector3 n = normal_group_averages[l] / group_indices.size();
+ if (n.dot(ray_normal) > normal_pre_split_threshold) {
+ found = true;
+ group_indices.push_back(corner_idx);
+ normal_group_averages[l] += ray_normal;
+ break;
+ }
+ }
+
+ if (!found) {
+ LocalVector<int> new_group;
+ new_group.push_back(corner_idx);
+ normal_group_indices.push_back(new_group);
+ normal_group_averages.push_back(ray_normal);
+ }
+ }
+
+ for (unsigned int k = 0; k < normal_group_indices.size(); k++) {
+ LocalVector<int> &group_indices = normal_group_indices[k];
+ Vector3 n = normal_group_averages[k] / group_indices.size();
+
+ if (vertex_normal.dot(n) < normal_split_threshold) {
+ split_vertex_indices.push_back(j);
+ split_vertex_normals.push_back(n);
+ int new_idx = split_vertex_count++;
+ for (unsigned int l = 0; l < group_indices.size(); l++) {
+ new_indices_ptr[group_indices[l]] = new_idx;
+ }
+ }
+ }
+
+ normal_group_indices.clear();
+ normal_group_averages.clear();
+ }
+ }
+
+ Surface::LOD lod;
+ lod.distance = MAX(mesh_error * scale, CMP_EPSILON2);
+ lod.indices = new_indices;
+ surfaces.write[i].lods.push_back(lod);
+ index_target = MAX(new_index_count, index_target) * 2;
+ last_index_count = new_index_count;
+
+ if (mesh_error == 0.0f) {
+ break;
+ }
+ }
+
+ surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals);
+ surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
+
+ for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
+ SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count);
+ }
+ }
+}
+
+bool ImporterMesh::has_mesh() const {
+ return mesh.is_valid();
+}
+
+Ref<ArrayMesh> ImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) {
+ ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
+
+ if (mesh.is_null()) {
+ if (p_base.is_valid()) {
+ mesh = p_base;
+ }
+ if (mesh.is_null()) {
+ mesh.instantiate();
+ }
+ mesh->set_name(get_name());
+ if (has_meta("import_id")) {
+ mesh->set_meta("import_id", get_meta("import_id"));
+ }
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ mesh->add_blend_shape(blend_shapes[i]);
+ }
+ mesh->set_blend_shape_mode(blend_shape_mode);
+ for (int i = 0; i < surfaces.size(); i++) {
+ Array bs_data;
+ if (surfaces[i].blend_shape_data.size()) {
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ bs_data.push_back(surfaces[i].blend_shape_data[j].arrays);
+ }
+ }
+ Dictionary lods;
+ if (surfaces[i].lods.size()) {
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices;
+ }
+ }
+
+ mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods, surfaces[i].flags);
+ if (surfaces[i].material.is_valid()) {
+ mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material);
+ }
+ if (surfaces[i].name != String()) {
+ mesh->surface_set_name(mesh->get_surface_count() - 1, surfaces[i].name);
+ }
+ }
+
+ mesh->set_lightmap_size_hint(lightmap_size_hint);
+
+ if (shadow_mesh.is_valid()) {
+ Ref<ArrayMesh> shadow = shadow_mesh->get_mesh();
+ mesh->set_shadow_mesh(shadow);
+ }
+ }
+
+ return mesh;
+}
+
+void ImporterMesh::clear() {
+ surfaces.clear();
+ blend_shapes.clear();
+ mesh.unref();
+}
+
+void ImporterMesh::create_shadow_mesh() {
+ if (shadow_mesh.is_valid()) {
+ shadow_mesh.unref();
+ }
+
+ //no shadow mesh for blendshapes
+ if (blend_shapes.size() > 0) {
+ return;
+ }
+ //no shadow mesh for skeletons
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].arrays[RS::ARRAY_BONES].get_type() != Variant::NIL) {
+ return;
+ }
+ if (surfaces[i].arrays[RS::ARRAY_WEIGHTS].get_type() != Variant::NIL) {
+ return;
+ }
+ }
+
+ shadow_mesh.instantiate();
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ LocalVector<int> vertex_remap;
+ Vector<Vector3> new_vertices;
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ int vertex_count = vertices.size();
+ {
+ Map<Vector3, int> unique_vertices;
+ const Vector3 *vptr = vertices.ptr();
+ for (int j = 0; j < vertex_count; j++) {
+ const Vector3 &v = vptr[j];
+
+ Map<Vector3, int>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ vertex_remap.push_back(E->get());
+ } else {
+ int vcount = unique_vertices.size();
+ unique_vertices[v] = vcount;
+ vertex_remap.push_back(vcount);
+ new_vertices.push_back(v);
+ }
+ }
+ }
+
+ Array new_surface;
+ new_surface.resize(RS::ARRAY_MAX);
+ Dictionary lods;
+
+ // print_line("original vertex count: " + itos(vertices.size()) + " new vertex count: " + itos(new_vertices.size()));
+
+ new_surface[RS::ARRAY_VERTEX] = new_vertices;
+
+ Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ if (indices.size()) {
+ int index_count = indices.size();
+ const int *index_rptr = indices.ptr();
+ Vector<int> new_indices;
+ new_indices.resize(indices.size());
+ int *index_wptr = new_indices.ptrw();
+
+ for (int j = 0; j < index_count; j++) {
+ int index = index_rptr[j];
+ ERR_FAIL_INDEX(index, vertex_count);
+ index_wptr[j] = vertex_remap[index];
+ }
+
+ new_surface[RS::ARRAY_INDEX] = new_indices;
+
+ // Make sure the same LODs as the full version are used.
+ // This makes it more coherent between rendered model and its shadows.
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ indices = surfaces[i].lods[j].indices;
+
+ index_count = indices.size();
+ index_rptr = indices.ptr();
+ new_indices.resize(indices.size());
+ index_wptr = new_indices.ptrw();
+
+ for (int k = 0; k < index_count; k++) {
+ int index = index_rptr[k];
+ ERR_FAIL_INDEX(index, vertex_count);
+ index_wptr[k] = vertex_remap[index];
+ }
+
+ lods[surfaces[i].lods[j].distance] = new_indices;
+ }
+ }
+
+ shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name, surfaces[i].flags);
+ }
+}
+
+Ref<ImporterMesh> ImporterMesh::get_shadow_mesh() const {
+ return shadow_mesh;
+}
+
+void ImporterMesh::_set_data(const Dictionary &p_data) {
+ clear();
+ if (p_data.has("blend_shape_names")) {
+ blend_shapes = p_data["blend_shape_names"];
+ }
+ if (p_data.has("surfaces")) {
+ Array surface_arr = p_data["surfaces"];
+ for (int i = 0; i < surface_arr.size(); i++) {
+ Dictionary s = surface_arr[i];
+ ERR_CONTINUE(!s.has("primitive"));
+ ERR_CONTINUE(!s.has("arrays"));
+ Mesh::PrimitiveType prim = Mesh::PrimitiveType(int(s["primitive"]));
+ ERR_CONTINUE(prim >= Mesh::PRIMITIVE_MAX);
+ Array arr = s["arrays"];
+ Dictionary lods;
+ String name;
+ if (s.has("name")) {
+ name = s["name"];
+ }
+ if (s.has("lods")) {
+ lods = s["lods"];
+ }
+ Array b_shapes;
+ if (s.has("b_shapes")) {
+ b_shapes = s["b_shapes"];
+ }
+ Ref<Material> material;
+ if (s.has("material")) {
+ material = s["material"];
+ }
+ uint32_t flags = 0;
+ if (s.has("flags")) {
+ flags = s["flags"];
+ }
+ add_surface(prim, arr, b_shapes, lods, material, name, flags);
+ }
+ }
+}
+Dictionary ImporterMesh::_get_data() const {
+ Dictionary data;
+ if (blend_shapes.size()) {
+ data["blend_shape_names"] = blend_shapes;
+ }
+ Array surface_arr;
+ for (int i = 0; i < surfaces.size(); i++) {
+ Dictionary d;
+ d["primitive"] = surfaces[i].primitive;
+ d["arrays"] = surfaces[i].arrays;
+ if (surfaces[i].blend_shape_data.size()) {
+ Array bs_data;
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ bs_data.push_back(surfaces[i].blend_shape_data[j].arrays);
+ }
+ d["blend_shapes"] = bs_data;
+ }
+ if (surfaces[i].lods.size()) {
+ Dictionary lods;
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices;
+ }
+ d["lods"] = lods;
+ }
+
+ if (surfaces[i].material.is_valid()) {
+ d["material"] = surfaces[i].material;
+ }
+
+ if (surfaces[i].name != String()) {
+ d["name"] = surfaces[i].name;
+ }
+
+ if (surfaces[i].flags != 0) {
+ d["flags"] = surfaces[i].flags;
+ }
+
+ surface_arr.push_back(d);
+ }
+ data["surfaces"] = surface_arr;
+ return data;
+}
+
+Vector<Face3> ImporterMesh::get_faces() const {
+ Vector<Face3> faces;
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
+ Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX];
+ Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX];
+ if (indices.size()) {
+ for (int j = 0; j < indices.size(); j += 3) {
+ Face3 f;
+ f.vertex[0] = vertices[indices[j + 0]];
+ f.vertex[1] = vertices[indices[j + 1]];
+ f.vertex[2] = vertices[indices[j + 2]];
+ faces.push_back(f);
+ }
+ } else {
+ for (int j = 0; j < vertices.size(); j += 3) {
+ Face3 f;
+ f.vertex[0] = vertices[j + 0];
+ f.vertex[1] = vertices[j + 1];
+ f.vertex[2] = vertices[j + 2];
+ faces.push_back(f);
+ }
+ }
+ }
+ }
+
+ return faces;
+}
+
+Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const {
+ ERR_FAIL_COND_V(!Mesh::convex_decomposition_function, Vector<Ref<Shape3D>>());
+
+ const Vector<Face3> faces = get_faces();
+ int face_count = faces.size();
+
+ Vector<Vector3> vertices;
+ uint32_t vertex_count = 0;
+ vertices.resize(face_count * 3);
+ Vector<uint32_t> indices;
+ indices.resize(face_count * 3);
+ {
+ Map<Vector3, uint32_t> vertex_map;
+ Vector3 *vertex_w = vertices.ptrw();
+ uint32_t *index_w = indices.ptrw();
+ for (int i = 0; i < face_count; i++) {
+ for (int j = 0; j < 3; j++) {
+ const Vector3 &vertex = faces[i].vertex[j];
+ Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex);
+ uint32_t index;
+ if (found_vertex) {
+ index = found_vertex->get();
+ } else {
+ index = ++vertex_count;
+ vertex_map[vertex] = index;
+ vertex_w[index] = vertex;
+ }
+ index_w[i * 3 + j] = index;
+ }
+ }
+ }
+ vertices.resize(vertex_count);
+
+ Vector<Vector<Vector3>> decomposed = Mesh::convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), face_count, p_settings, nullptr);
+
+ Vector<Ref<Shape3D>> ret;
+
+ for (int i = 0; i < decomposed.size(); i++) {
+ Ref<ConvexPolygonShape3D> shape;
+ shape.instantiate();
+ shape->set_points(decomposed[i]);
+ ret.push_back(shape);
+ }
+
+ return ret;
+}
+
+Ref<Shape3D> ImporterMesh::create_trimesh_shape() const {
+ Vector<Face3> faces = get_faces();
+ if (faces.size() == 0) {
+ return Ref<Shape3D>();
+ }
+
+ Vector<Vector3> face_points;
+ face_points.resize(faces.size() * 3);
+
+ for (int i = 0; i < face_points.size(); i += 3) {
+ Face3 f = faces.get(i / 3);
+ face_points.set(i, f.vertex[0]);
+ face_points.set(i + 1, f.vertex[1]);
+ face_points.set(i + 2, f.vertex[2]);
+ }
+
+ Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D);
+ shape->set_faces(face_points);
+ return shape;
+}
+
+Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
+ Vector<Face3> faces = get_faces();
+ if (faces.size() == 0) {
+ return Ref<NavigationMesh>();
+ }
+
+ Map<Vector3, int> unique_vertices;
+ LocalVector<int> face_indices;
+
+ for (int i = 0; i < faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = faces[i].vertex[j];
+ int idx;
+ if (unique_vertices.has(v)) {
+ idx = unique_vertices[v];
+ } else {
+ idx = unique_vertices.size();
+ unique_vertices[v] = idx;
+ }
+ face_indices.push_back(idx);
+ }
+ }
+
+ Vector<Vector3> vertices;
+ vertices.resize(unique_vertices.size());
+ for (const KeyValue<Vector3, int> &E : unique_vertices) {
+ vertices.write[E.value] = E.key;
+ }
+
+ Ref<NavigationMesh> nm;
+ nm.instantiate();
+ nm->set_vertices(vertices);
+
+ Vector<int> v3;
+ v3.resize(3);
+ for (uint32_t i = 0; i < face_indices.size(); i += 3) {
+ v3.write[0] = face_indices[i + 0];
+ v3.write[1] = face_indices[i + 1];
+ v3.write[2] = face_indices[i + 2];
+ nm->add_polygon(v3);
+ }
+
+ return nm;
+}
+
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
+
+struct EditorSceneFormatImporterMeshLightmapSurface {
+ Ref<Material> material;
+ LocalVector<SurfaceTool::Vertex> vertices;
+ Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
+ uint32_t format = 0;
+ String name;
+};
+
+Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) {
+ ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
+
+ LocalVector<float> vertices;
+ LocalVector<float> normals;
+ LocalVector<int> indices;
+ LocalVector<float> uv;
+ LocalVector<Pair<int, int>> uv_indices;
+
+ Vector<EditorSceneFormatImporterMeshLightmapSurface> lightmap_surfaces;
+
+ // Keep only the scale
+ Basis basis = p_base_transform.get_basis();
+ Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+
+ Transform3D transform;
+ transform.scale(scale);
+
+ Basis normal_basis = transform.basis.inverse().transposed();
+
+ for (int i = 0; i < get_surface_count(); i++) {
+ EditorSceneFormatImporterMeshLightmapSurface s;
+ s.primitive = get_surface_primitive_type(i);
+
+ ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap.");
+ Array arrays = get_surface_arrays(i);
+ s.material = get_surface_material(i);
+ s.name = get_surface_name(i);
+
+ SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
+
+ PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
+ int vc = rvertices.size();
+
+ PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
+
+ int vertex_ofs = vertices.size() / 3;
+
+ vertices.resize((vertex_ofs + vc) * 3);
+ normals.resize((vertex_ofs + vc) * 3);
+ uv_indices.resize(vertex_ofs + vc);
+
+ for (int j = 0; j < vc; j++) {
+ Vector3 v = transform.xform(rvertices[j]);
+ Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+ vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+ vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+ vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+ normals[(j + vertex_ofs) * 3 + 0] = n.x;
+ normals[(j + vertex_ofs) * 3 + 1] = n.y;
+ normals[(j + vertex_ofs) * 3 + 2] = n.z;
+ uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
+ }
+
+ PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
+ int ic = rindices.size();
+
+ float eps = 1.19209290e-7F; // Taken from xatlas.h
+ if (ic == 0) {
+ for (int j = 0; j < vc / 3; j++) {
+ Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+ Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+ Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
+ continue;
+ }
+
+ indices.push_back(vertex_ofs + j * 3 + 0);
+ indices.push_back(vertex_ofs + j * 3 + 1);
+ indices.push_back(vertex_ofs + j * 3 + 2);
+ }
+
+ } else {
+ for (int j = 0; j < ic / 3; j++) {
+ Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+ Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+ Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
+ continue;
+ }
+
+ indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
+ }
+ }
+
+ lightmap_surfaces.push_back(s);
+ }
+
+ //unwrap
+
+ bool use_cache = true; // Used to request cache generation and to know if cache was used
+ uint8_t *gen_cache;
+ int gen_cache_size;
+ float *gen_uvs;
+ int *gen_vertices;
+ int *gen_indices;
+ int gen_vertex_count;
+ int gen_index_count;
+ int size_x;
+ int size_y;
+
+ bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
+
+ if (!ok) {
+ return ERR_CANT_CREATE;
+ }
+
+ //remove surfaces
+ clear();
+
+ //create surfacetools for each surface..
+ LocalVector<Ref<SurfaceTool>> surfaces_tools;
+
+ for (int i = 0; i < lightmap_surfaces.size(); i++) {
+ Ref<SurfaceTool> st;
+ st.instantiate();
+ st->begin(Mesh::PRIMITIVE_TRIANGLES);
+ st->set_material(lightmap_surfaces[i].material);
+ st->set_meta("name", lightmap_surfaces[i].name);
+ surfaces_tools.push_back(st); //stay there
+ }
+
+ print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
+ //go through all indices
+ for (int i = 0; i < gen_index_count; i += 3) {
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
+
+ ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
+
+ int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first;
+
+ for (int j = 0; j < 3; j++) {
+ SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
+
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
+ surfaces_tools[surface]->set_color(v.color);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
+ surfaces_tools[surface]->set_uv(v.uv);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
+ surfaces_tools[surface]->set_normal(v.normal);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
+ Plane t;
+ t.normal = v.tangent;
+ t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
+ surfaces_tools[surface]->set_tangent(t);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
+ surfaces_tools[surface]->set_bones(v.bones);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
+ surfaces_tools[surface]->set_weights(v.weights);
+ }
+
+ Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
+ surfaces_tools[surface]->set_uv2(uv2);
+
+ surfaces_tools[surface]->add_vertex(v.vertex);
+ }
+ }
+
+ //generate surfaces
+ for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+ surfaces_tools[i]->index();
+ Array arrays = surfaces_tools[i]->commit_to_arrays();
+ add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name"));
+ }
+
+ set_lightmap_size_hint(Size2(size_x, size_y));
+
+ if (gen_cache_size > 0) {
+ r_dst_cache.resize(gen_cache_size);
+ memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+ memfree(gen_cache);
+ }
+
+ if (!use_cache) {
+ // Cache was not used, free the buffers
+ memfree(gen_vertices);
+ memfree(gen_indices);
+ memfree(gen_uvs);
+ }
+
+ return OK;
+}
+
+void ImporterMesh::set_lightmap_size_hint(const Size2i &p_size) {
+ lightmap_size_hint = p_size;
+}
+
+Size2i ImporterMesh::get_lightmap_size_hint() const {
+ return lightmap_size_hint;
+}
+
+void ImporterMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ImporterMesh::add_blend_shape);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ImporterMesh::get_blend_shape_count);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_name", "blend_shape_idx"), &ImporterMesh::get_blend_shape_name);
+
+ ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ImporterMesh::set_blend_shape_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ImporterMesh::get_blend_shape_mode);
+
+ ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &ImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("get_surface_count"), &ImporterMesh::get_surface_count);
+ ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &ImporterMesh::get_surface_primitive_type);
+ ClassDB::bind_method(D_METHOD("get_surface_name", "surface_idx"), &ImporterMesh::get_surface_name);
+ ClassDB::bind_method(D_METHOD("get_surface_arrays", "surface_idx"), &ImporterMesh::get_surface_arrays);
+ ClassDB::bind_method(D_METHOD("get_surface_blend_shape_arrays", "surface_idx", "blend_shape_idx"), &ImporterMesh::get_surface_blend_shape_arrays);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_count", "surface_idx"), &ImporterMesh::get_surface_lod_count);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_size", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_size);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_indices", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_indices);
+ ClassDB::bind_method(D_METHOD("get_surface_material", "surface_idx"), &ImporterMesh::get_surface_material);
+ ClassDB::bind_method(D_METHOD("get_surface_format", "surface_idx"), &ImporterMesh::get_surface_format);
+
+ ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name);
+ ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material);
+
+ ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>()));
+ ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear);
+
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &ImporterMesh::_set_data);
+ ClassDB::bind_method(D_METHOD("_get_data"), &ImporterMesh::_get_data);
+
+ ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint);
+ ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
+}
diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h
new file mode 100644
index 0000000000..8576312a8a
--- /dev/null
+++ b/scene/resources/importer_mesh.h
@@ -0,0 +1,133 @@
+/*************************************************************************/
+/* importer_mesh.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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 SCENE_IMPORTER_MESH_H
+#define SCENE_IMPORTER_MESH_H
+
+#include "core/io/resource.h"
+#include "core/templates/local_vector.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+#include "scene/resources/convex_polygon_shape_3d.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/navigation_mesh.h"
+
+#include <cstdint>
+
+// The following classes are used by importers instead of ArrayMesh and MeshInstance3D
+// so the data is not registered (hence, quality loss), importing happens faster and
+// its easier to modify before saving
+
+class ImporterMesh : public Resource {
+ GDCLASS(ImporterMesh, Resource)
+
+ struct Surface {
+ Mesh::PrimitiveType primitive;
+ Array arrays;
+ struct BlendShape {
+ Array arrays;
+ };
+ Vector<BlendShape> blend_shape_data;
+ struct LOD {
+ Vector<int> indices;
+ float distance = 0.0f;
+ };
+ Vector<LOD> lods;
+ Ref<Material> material;
+ String name;
+ uint32_t flags = 0;
+
+ struct LODComparator {
+ _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const {
+ return l.distance < r.distance;
+ }
+ };
+
+ void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
+ static void _split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
+ };
+ Vector<Surface> surfaces;
+ Vector<String> blend_shapes;
+ Mesh::BlendShapeMode blend_shape_mode = Mesh::BLEND_SHAPE_MODE_NORMALIZED;
+
+ Ref<ArrayMesh> mesh;
+
+ Ref<ImporterMesh> shadow_mesh;
+
+ Size2i lightmap_size_hint;
+
+protected:
+ void _set_data(const Dictionary &p_data);
+ Dictionary _get_data() const;
+
+ static void _bind_methods();
+
+public:
+ void add_blend_shape(const String &p_name);
+ int get_blend_shape_count() const;
+ String get_blend_shape_name(int p_blend_shape) const;
+
+ void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0);
+ int get_surface_count() const;
+
+ void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode);
+ Mesh::BlendShapeMode get_blend_shape_mode() const;
+
+ Mesh::PrimitiveType get_surface_primitive_type(int p_surface);
+ String get_surface_name(int p_surface) const;
+ void set_surface_name(int p_surface, const String &p_name);
+ Array get_surface_arrays(int p_surface) const;
+ Array get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const;
+ int get_surface_lod_count(int p_surface) const;
+ Vector<int> get_surface_lod_indices(int p_surface, int p_lod) const;
+ float get_surface_lod_size(int p_surface, int p_lod) const;
+ Ref<Material> get_surface_material(int p_surface) const;
+ uint32_t get_surface_format(int p_surface) const;
+
+ void set_surface_material(int p_surface, const Ref<Material> &p_material);
+
+ void generate_lods(float p_normal_merge_angle, float p_normal_split_angle);
+
+ void create_shadow_mesh();
+ Ref<ImporterMesh> get_shadow_mesh() const;
+
+ Vector<Face3> get_faces() const;
+ Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const;
+ Ref<Shape3D> create_trimesh_shape() const;
+ Ref<NavigationMesh> create_navigation_mesh();
+ Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
+
+ void set_lightmap_size_hint(const Size2i &p_size);
+ Size2i get_lightmap_size_hint() const;
+
+ bool has_mesh() const;
+ Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>());
+ void clear();
+};
+#endif // SCENE_IMPORTER_MESH_H
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 1d2a2ef26c..abb3381c4e 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -32,11 +32,6 @@
#include "core/config/engine.h"
#include "core/version.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
-#endif
-
#include "scene/main/scene_tree.h"
#include "scene/scene_string_names.h"
@@ -80,6 +75,9 @@ void Material::_validate_property(PropertyInfo &property) const {
if (!_can_do_next_pass() && property.name == "next_pass") {
property.usage = PROPERTY_USAGE_NONE;
}
+ if (!_can_use_render_priority() && property.name == "render_priority") {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
}
void Material::inspect_native_shader_code() {
@@ -268,19 +266,13 @@ void ShaderMaterial::_bind_methods() {
}
void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
-#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
-#else
- const String quote_style = "\"";
-#endif
-
String f = p_function.operator String();
if ((f == "get_shader_param" || f == "set_shader_param") && p_idx == 0) {
if (shader.is_valid()) {
List<PropertyInfo> pl;
shader->get_param_list(&pl);
for (const PropertyInfo &E : pl) {
- r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style));
+ r_options->push_back(E.name.replace_first("shader_param/", "").quote());
}
}
}
@@ -291,6 +283,10 @@ bool ShaderMaterial::_can_do_next_pass() const {
return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL;
}
+bool ShaderMaterial::_can_use_render_priority() const {
+ return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL;
+}
+
Shader::Mode ShaderMaterial::get_shader_mode() const {
if (shader.is_valid()) {
return shader->get_mode();
@@ -790,12 +786,10 @@ void BaseMaterial3D::_update_shader() {
}
} break;
case BILLBOARD_FIXED_Y: {
- code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)), 0.0),WORLD_MATRIX[3]);\n";
+ code += " MODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), CAMERA_MATRIX[2].xyz)),0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(normalize(cross(CAMERA_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))),0.0),WORLD_MATRIX[3]);\n";
if (flags[FLAG_BILLBOARD_KEEP_SCALE]) {
- code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, 1.0, 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
- } else {
- code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1.0, 0.0, 0.0, 0.0),vec4(0.0, 1.0/length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0),vec4(0.0, 0.0, 0.0 ,1.0));\n";
+ code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0),vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0),vec4(0.0, 0.0, 0.0, 1.0));\n";
}
} break;
case BILLBOARD_PARTICLES: {
@@ -817,7 +811,7 @@ void BaseMaterial3D::_update_shader() {
code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
code += " }";
code += " UV /= vec2(h_frames, v_frames);\n";
- code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n";
} break;
case BILLBOARD_MAX:
break; // Internal value, skip.
@@ -963,7 +957,9 @@ void BaseMaterial3D::_update_shader() {
} else {
code += " float depth = 1.0 - texture(texture_heightmap, base_uv).r;\n";
}
- code += " vec2 ofs = base_uv - view_dir.xy / view_dir.z * (depth * heightmap_scale);\n";
+ // Use offset limiting to improve the appearance of non-deep parallax.
+ // This reduces the impression of depth, but avoids visible warping in the distance.
+ code += " vec2 ofs = base_uv - view_dir.xy * depth * heightmap_scale;\n";
}
code += " base_uv=ofs;\n";
@@ -1301,7 +1297,7 @@ void BaseMaterial3D::flush_changes() {
void BaseMaterial3D::_queue_shader_change() {
MutexLock lock(material_mutex);
- if (!element.in_list()) {
+ if (is_initialized && !element.in_list()) {
dirty_materials->add(&element);
}
}
@@ -2777,6 +2773,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
flags[FLAG_USE_TEXTURE_REPEAT] = true;
+ is_initialized = true;
_queue_shader_change();
}
diff --git a/scene/resources/material.h b/scene/resources/material.h
index e2838e1399..798f7568df 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -53,6 +53,7 @@ protected:
_FORCE_INLINE_ RID _get_material() const { return material; }
static void _bind_methods();
virtual bool _can_do_next_pass() const { return false; }
+ virtual bool _can_use_render_priority() const { return false; }
void _validate_property(PropertyInfo &property) const override;
@@ -93,6 +94,7 @@ protected:
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
virtual bool _can_do_next_pass() const override;
+ virtual bool _can_use_render_priority() const override;
void _shader_changed();
@@ -440,6 +442,7 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
+ bool is_initialized = false;
bool orm;
Color albedo;
@@ -534,6 +537,7 @@ protected:
static void _bind_methods();
void _validate_property(PropertyInfo &property) const override;
virtual bool _can_do_next_pass() const override { return true; }
+ virtual bool _can_use_render_priority() const override { return true; }
public:
void set_albedo(const Color &p_albedo);
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index ad589a605e..7ffe0b03e1 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -38,7 +38,7 @@
#include <stdlib.h>
-Mesh::ConvexDecompositionFunc Mesh::convex_composition_function = nullptr;
+Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;
Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
if (triangle_mesh.is_valid()) {
@@ -156,75 +156,19 @@ void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) {
}
}
-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);
-}
-
Vector<Face3> Mesh::get_faces() const {
Ref<TriangleMesh> tm = generate_triangle_mesh();
if (tm.is_valid()) {
return tm->get_faces();
}
return Vector<Face3>();
- /*
- for (int i=0;i<surfaces.size();i++) {
- if (RenderingServer::get_singleton()->mesh_surface_get_primitive_type( mesh, i ) != RenderingServer::PRIMITIVE_TRIANGLES )
- continue;
-
- Vector<int> indices;
- Vector<Vector3> vertices;
-
- vertices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_VERTEX);
-
- int len=RenderingServer::get_singleton()->mesh_surface_get_array_index_len(mesh, i);
- bool has_indices;
-
- if (len>0) {
- indices=RenderingServer::get_singleton()->mesh_surface_get_array(mesh, i,RenderingServer::ARRAY_INDEX);
- has_indices=true;
-
- } else {
- len=vertices.size();
- has_indices=false;
- }
-
- if (len<=0)
- continue;
-
- const int* indicesr = indices.ptr();
- const int *indicesptr = indicesr.ptr();
-
- const Vector3* verticesr = vertices.ptr();
- const Vector3 *verticesptr = verticesr.ptr();
-
- int old_faces=faces.size();
- int new_faces=old_faces+(len/3);
-
- faces.resize(new_faces);
-
- Face3* facesw = faces.ptrw();
- Face3 *facesptr=facesw.ptr();
-
-
- for (int i=0;i<len/3;i++) {
- Face3 face;
-
- for (int j=0;j<3;j++) {
- int idx=i*3+j;
- face.vertex[j] = has_indices ? verticesptr[ indicesptr[ idx ] ] : verticesptr[idx];
- }
-
- facesptr[i+old_faces]=face;
- }
-
- }
-*/
}
Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const {
if (p_simplify) {
- Vector<Ref<Shape3D>> decomposed = convex_decompose(1);
+ ConvexDecompositionSettings settings;
+ settings.max_convex_hulls = 1;
+ Vector<Ref<Shape3D>> decomposed = convex_decompose(settings);
if (decomposed.size() == 1) {
return decomposed[0];
} else {
@@ -429,8 +373,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
//normalize
- for (Map<Vector3, Vector3>::Element *E = normal_accum.front(); E; E = E->next()) {
- E->get().normalize();
+ for (KeyValue<Vector3, Vector3> &E : normal_accum) {
+ E.value.normalize();
}
//displace normals
@@ -543,6 +487,7 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_FORMAT_BLEND_SHAPE_MASK);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BASE);
+ BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM_BITS);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM0_SHIFT);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM1_SHIFT);
BIND_ENUM_CONSTANT(ARRAY_FORMAT_CUSTOM2_SHIFT);
@@ -564,36 +509,37 @@ void Mesh::clear_cache() const {
debug_lines.clear();
}
-Vector<Ref<Shape3D>> Mesh::convex_decompose(int p_max_convex_hulls) const {
- ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape3D>>());
+Vector<Ref<Shape3D>> Mesh::convex_decompose(const ConvexDecompositionSettings &p_settings) const {
+ ERR_FAIL_COND_V(!convex_decomposition_function, Vector<Ref<Shape3D>>());
- const Vector<Face3> faces = get_faces();
-
- Vector<Vector<Face3>> decomposed = convex_composition_function(faces, p_max_convex_hulls);
+ Ref<TriangleMesh> tm = generate_triangle_mesh();
+ ERR_FAIL_COND_V(!tm.is_valid(), Vector<Ref<Shape3D>>());
- Vector<Ref<Shape3D>> ret;
+ const Vector<TriangleMesh::Triangle> &triangles = tm->get_triangles();
+ int triangle_count = triangles.size();
- for (int i = 0; i < decomposed.size(); i++) {
- Set<Vector3> points;
- for (int j = 0; j < decomposed[i].size(); j++) {
- points.insert(decomposed[i][j].vertex[0]);
- points.insert(decomposed[i][j].vertex[1]);
- points.insert(decomposed[i][j].vertex[2]);
- }
-
- Vector<Vector3> convex_points;
- convex_points.resize(points.size());
- {
- Vector3 *w = convex_points.ptrw();
- int idx = 0;
- for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) {
- w[idx++] = E->get();
+ Vector<uint32_t> indices;
+ {
+ indices.resize(triangle_count * 3);
+ uint32_t *w = indices.ptrw();
+ for (int i = 0; i < triangle_count; i++) {
+ for (int j = 0; j < 3; j++) {
+ w[i * 3 + j] = triangles[i].indices[j];
}
}
+ }
+
+ const Vector<Vector3> &vertices = tm->get_vertices();
+ int vertex_count = vertices.size();
+ Vector<Vector<Vector3>> decomposed = convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), triangle_count, p_settings, nullptr);
+
+ Vector<Ref<Shape3D>> ret;
+
+ for (int i = 0; i < decomposed.size(); i++) {
Ref<ConvexPolygonShape3D> shape;
shape.instantiate();
- shape->set_points(convex_points);
+ shape->set_points(decomposed[i]);
ret.push_back(shape);
}
@@ -1631,7 +1577,7 @@ void ArrayMesh::regen_normal_maps() {
}
//dirty hack
-bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL;
+bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = nullptr;
struct ArrayMeshLightmapSurface {
Ref<Material> material;
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 27b0eb098b..8d5571d67c 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -105,6 +105,7 @@ public:
ARRAY_FORMAT_BLEND_SHAPE_MASK = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK,
ARRAY_FORMAT_CUSTOM_BASE = RS::ARRAY_FORMAT_CUSTOM_BASE,
+ ARRAY_FORMAT_CUSTOM_BITS = RS::ARRAY_FORMAT_CUSTOM_BITS,
ARRAY_FORMAT_CUSTOM0_SHIFT = RS::ARRAY_FORMAT_CUSTOM0_SHIFT,
ARRAY_FORMAT_CUSTOM1_SHIFT = RS::ARRAY_FORMAT_CUSTOM1_SHIFT,
ARRAY_FORMAT_CUSTOM2_SHIFT = RS::ARRAY_FORMAT_CUSTOM2_SHIFT,
@@ -131,7 +132,6 @@ 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 Dictionary surface_get_lods(int p_surface) const = 0;
@@ -159,11 +159,42 @@ public:
Size2i get_lightmap_size_hint() const;
void clear_cache() const;
- typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, int p_max_convex_hulls);
+ struct ConvexDecompositionSettings {
+ enum Mode : int {
+ CONVEX_DECOMPOSITION_MODE_VOXEL = 0,
+ CONVEX_DECOMPOSITION_MODE_TETRAHEDRON
+ };
+
+ /// Maximum concavity. [Range: 0.0 -> 1.0]
+ real_t max_concavity = 1.0;
+ /// Controls the bias toward clipping along symmetry planes. [Range: 0.0 -> 1.0]
+ real_t symmetry_planes_clipping_bias = 0.05;
+ /// Controls the bias toward clipping along revolution axes. [Range: 0.0 -> 1.0]
+ real_t revolution_axes_clipping_bias = 0.05;
+ real_t min_volume_per_convex_hull = 0.0001;
+ /// Maximum number of voxels generated during the voxelization stage.
+ uint32_t resolution = 10'000;
+ uint32_t max_num_vertices_per_convex_hull = 32;
+ /// Controls the granularity of the search for the "best" clipping plane.
+ /// [Range: 1 -> 16]
+ uint32_t plane_downsampling = 4;
+ /// Controls the precision of the convex-hull generation process during the
+ /// clipping plane selection stage.
+ /// [Range: 1 -> 16]
+ uint32_t convexhull_downsampling = 4;
+ /// enable/disable normalizing the mesh before applying the convex decomposition.
+ bool normalize_mesh = false;
+ Mode mode = CONVEX_DECOMPOSITION_MODE_VOXEL;
+ bool convexhull_approximation = true;
+ /// This is the maximum number of convex hulls to produce from the merge operation.
+ uint32_t max_convex_hulls = 1;
+ bool project_hull_vertices = true;
+ };
+ typedef Vector<Vector<Vector3>> (*ConvexDecompositionFunc)(const real_t *p_vertices, int p_vertex_count, const uint32_t *p_triangles, int p_triangle_count, const ConvexDecompositionSettings &p_settings, Vector<Vector<uint32_t>> *r_convex_indices);
- static ConvexDecompositionFunc convex_composition_function;
+ static ConvexDecompositionFunc convex_decomposition_function;
- Vector<Ref<Shape3D>> convex_decompose(int p_max_convex_hulls = -1) const;
+ Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const;
virtual int get_builtin_bind_pose_count() const;
virtual Transform3D get_builtin_bind_pose(int p_index) const;
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index 33c9ca6d1e..309670e0b1 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -43,6 +43,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
set_item_name(idx, p_value);
} else if (what == "mesh") {
set_item_mesh(idx, p_value);
+ } else if (what == "mesh_transform") {
+ set_item_mesh_transform(idx, p_value);
} else if (what == "shape") {
Vector<ShapeData> shapes;
ShapeData sd;
@@ -77,6 +79,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_name(idx);
} else if (what == "mesh") {
r_ret = get_item_mesh(idx);
+ } else if (what == "mesh_transform") {
+ r_ret = get_item_mesh_transform(idx);
} else if (what == "shapes") {
r_ret = _get_item_shapes(idx);
} else if (what == "navmesh") {
@@ -93,8 +97,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
}
void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
- for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) {
- String name = "item/" + itos(E->key()) + "/";
+ for (const KeyValue<int, Item> &E : item_map) {
+ String name = "item/" + itos(E.key) + "/";
p_list->push_back(PropertyInfo(Variant::STRING, name + "name"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform"));
@@ -127,6 +131,14 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
notify_property_list_changed();
}
+void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_transform) {
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+ item_map[p_item].mesh_transform = p_transform;
+ notify_change_to_owners();
+ emit_changed();
+ notify_property_list_changed();
+}
+
void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].shapes = p_shapes;
@@ -170,6 +182,11 @@ Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const {
return item_map[p_item].mesh;
}
+Transform3D MeshLibrary::get_item_mesh_transform(int p_item) const {
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform3D(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+ return item_map[p_item].mesh_transform;
+}
+
Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].shapes;
@@ -213,17 +230,17 @@ Vector<int> MeshLibrary::get_item_list() const {
Vector<int> ret;
ret.resize(item_map.size());
int idx = 0;
- for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) {
- ret.write[idx++] = E->key();
+ for (const KeyValue<int, Item> &E : item_map) {
+ ret.write[idx++] = E.key;
}
return ret;
}
int MeshLibrary::find_item_by_name(const String &p_name) const {
- for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) {
- if (E->get().name == p_name) {
- return E->key();
+ for (const KeyValue<int, Item> &E : item_map) {
+ if (E.value.name == p_name) {
+ return E.key;
}
}
return -1;
@@ -271,12 +288,14 @@ void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item);
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh);
+ ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform);
ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh);
ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform);
ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes);
ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview);
ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name);
ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh);
+ ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform);
ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh);
ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform);
ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes);
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index 1e8a6bf3ff..c25df757e9 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -52,6 +52,7 @@ public:
Vector<ShapeData> shapes;
Ref<Texture2D> preview;
Transform3D navmesh_transform;
+ Transform3D mesh_transform;
Ref<NavigationMesh> navmesh;
};
@@ -72,12 +73,14 @@ public:
void create_item(int p_item);
void set_item_name(int p_item, const String &p_name);
void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh);
+ void set_item_mesh_transform(int p_item, const Transform3D &p_transform);
void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh);
void set_item_navmesh_transform(int p_item, const Transform3D &p_transform);
void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes);
void set_item_preview(int p_item, const Ref<Texture2D> &p_preview);
String get_item_name(int p_item) const;
Ref<Mesh> get_item_mesh(int p_item) const;
+ Transform3D get_item_mesh_transform(int p_item) const;
Ref<NavigationMesh> get_item_navmesh(int p_item) const;
Transform3D get_item_navmesh_transform(int p_item) const;
Vector<ShapeData> get_item_shapes(int p_item) const;
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 00cee9269b..d87056f55d 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -41,6 +41,8 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
continue;
}
Array arr = p_mesh->surface_get_arrays(i);
+ ERR_CONTINUE(arr.size() != Mesh::ARRAY_MAX);
+
Vector<Vector3> varr = arr[Mesh::ARRAY_VERTEX];
Vector<int> iarr = arr[Mesh::ARRAY_INDEX];
if (varr.size() == 0 || iarr.size() == 0) {
@@ -367,10 +369,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
}
List<Vector3> lines;
- for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) {
- if (E->get()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
+ for (const KeyValue<_EdgeKey, bool> &E : edge_map) {
+ if (E.value) {
+ lines.push_back(E.key.from);
+ lines.push_back(E.key.to);
}
}
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index 1cdf7a07ed..009239838f 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -85,7 +85,7 @@ protected:
float cell_size = 0.3f;
float cell_height = 0.2f;
float agent_height = 2.0f;
- float agent_radius = 0.6f;
+ float agent_radius = 1.0f;
float agent_max_climb = 0.9f;
float agent_max_slope = 45.0f;
float region_min_size = 8.0f;
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index e74f759855..60cda637ca 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -99,8 +99,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
#endif
parent = nparent;
} else {
- // i == 0 is root node. Confirm that it doesn't have a parent defined.
+ // i == 0 is root node.
ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name]));
+ ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name]));
}
Node *node = nullptr;
@@ -298,8 +299,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
- for (Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.front(); E; E = E->next()) {
- E->get()->setup_local_to_scene();
+ for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) {
+ E.value->setup_local_to_scene();
}
//do connections
@@ -383,11 +384,11 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// save the child instantiated scenes that are chosen as editable, so they can be restored
// upon load back
- if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) {
+ if (p_node != p_owner && p_node->get_scene_file_path() != String() && p_owner->is_editable_instance(p_node)) {
editable_instances.push_back(p_owner->get_path_to(p_node));
// Node is the root of an editable instance.
is_editable_instance = true;
- } else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
+ } else if (p_node->get_owner() && p_owner->is_ancestor_of(p_node->get_owner()) && p_owner->is_editable_instance(p_node->get_owner())) {
// Node is part of an editable instance.
is_editable_instance = true;
}
@@ -436,14 +437,14 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
}
}
- if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
+ if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
if (p_node->get_scene_instance_load_placeholder()) {
//it's a placeholder, use the placeholder path
- nd.instance = _vm_get_variant(p_node->get_filename(), variant_map);
+ nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map);
nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
} else {
//must instance ourselves
- Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
+ Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path());
if (!instance.is_valid()) {
return ERR_CANT_OPEN;
}
@@ -453,7 +454,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
}
n = nullptr;
} else {
- if (n->get_filename() != String()) {
+ if (n->get_scene_file_path() != String()) {
//is an instance
Ref<SceneState> state = n->get_scene_instance_state();
if (state.is_valid()) {
@@ -713,7 +714,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
ERR_CONTINUE(!common_parent);
- if (common_parent != p_owner && common_parent->get_filename() == String()) {
+ if (common_parent != p_owner && common_parent->get_scene_file_path() == String()) {
common_parent = common_parent->get_owner();
}
@@ -773,7 +774,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
nl = nullptr;
} else {
- if (nl->get_filename() != String()) {
+ if (nl->get_scene_file_path() != String()) {
//is an instance
Ref<SceneState> state = nl->get_scene_instance_state();
if (state.is_valid()) {
@@ -886,8 +887,8 @@ Error SceneState::pack(Node *p_scene) {
names.resize(name_map.size());
- for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) {
- names.write[E->get()] = E->key();
+ for (const KeyValue<StringName, int> &E : name_map) {
+ names.write[E.value] = E.key;
}
variants.resize(variant_map.size());
@@ -898,14 +899,14 @@ Error SceneState::pack(Node *p_scene) {
}
node_paths.resize(nodepath_map.size());
- for (Map<Node *, int>::Element *E = nodepath_map.front(); E; E = E->next()) {
- node_paths.write[E->get()] = scene->get_path_to(E->key());
+ for (const KeyValue<Node *, int> &E : nodepath_map) {
+ node_paths.write[E.value] = scene->get_path_to(E.key);
}
if (Engine::get_singleton()->is_editor_hint()) {
// Build node path cache
- for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) {
- node_path_cache[scene->get_path_to(E->key())] = E->get();
+ for (const KeyValue<Node *, int> &E : node_map) {
+ node_path_cache[scene->get_path_to(E.key)] = E.value;
}
}
@@ -976,9 +977,9 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
}
int SceneState::_find_base_scene_node_remap_key(int p_idx) const {
- for (Map<int, int>::Element *E = base_scene_node_remap.front(); E; E = E->next()) {
- if (E->value() == p_idx) {
- return E->key();
+ for (const KeyValue<int, int> &E : base_scene_node_remap) {
+ if (E.value == p_idx) {
+ return E.key;
}
}
return -1;
@@ -1651,7 +1652,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const {
}
if (get_path() != "" && get_path().find("::") == -1) {
- s->set_filename(get_path());
+ s->set_scene_file_path(get_path());
}
s->notification(Node::NOTIFICATION_INSTANCED);
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 0495a9e92c..d9ec0bfd69 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -741,7 +741,7 @@ void ParticlesMaterial::flush_changes() {
void ParticlesMaterial::_queue_shader_change() {
MutexLock lock(material_mutex);
- if (!element.in_list()) {
+ if (is_initialized && !element.in_list()) {
dirty_materials->add(&element);
}
}
@@ -1533,6 +1533,7 @@ ParticlesMaterial::ParticlesMaterial() :
current_key.invalid_key = 1;
+ is_initialized = true;
_queue_shader_change();
}
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index 8ab26aff77..36bc456978 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -226,6 +226,7 @@ private:
_FORCE_INLINE_ void _queue_shader_change();
_FORCE_INLINE_ bool _is_shader_dirty() const;
+ bool is_initialized = false;
Vector3 direction;
float spread;
float flatness;
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index e7da41db9d..f8be00f5fb 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset");
}
+uint32_t QuadMesh::surface_get_format(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, 1, 0);
+
+ return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV;
+}
+
QuadMesh::QuadMesh() {
primitive_type = PRIMITIVE_TRIANGLES;
}
@@ -1460,7 +1466,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
} else {
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
points.push_back(p);
- Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale);
+ Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale);
normals.push_back(normal.normalized());
};
ADD_TANGENT(z, 0.0, -x, 1.0)
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 7915cb0028..d447dad97a 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -285,6 +285,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ virtual uint32_t surface_get_format(int p_idx) const override;
+
QuadMesh();
void set_size(const Size2 &p_size);
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index b863a309c0..77d915aef9 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1653,55 +1653,55 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
#ifdef TOOLS_ENABLED
// Keep order from cached ids.
Set<String> cached_ids_found;
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
- String cached_id = E->key()->get_id_for_path(local_path);
+ for (KeyValue<RES, String> &E : external_resources) {
+ String cached_id = E.key->get_id_for_path(local_path);
if (cached_id == "" || cached_ids_found.has(cached_id)) {
- int sep_pos = E->get().find("_");
+ int sep_pos = E.value.find("_");
if (sep_pos != -1) {
- E->get() = E->get().substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
+ E.value = E.value.substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
} else {
- E->get() = "";
+ E.value = "";
}
} else {
- E->get() = cached_id;
+ E.value = cached_id;
cached_ids_found.insert(cached_id);
}
}
// Create IDs for non cached resources.
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
- if (cached_ids_found.has(E->get())) { // Already cached, go on.
+ for (KeyValue<RES, String> &E : external_resources) {
+ if (cached_ids_found.has(E.value)) { // Already cached, go on.
continue;
}
String attempt;
while (true) {
- attempt = E->get() + Resource::generate_scene_unique_id();
+ attempt = E.value + Resource::generate_scene_unique_id();
if (!cached_ids_found.has(attempt)) {
break;
}
}
cached_ids_found.insert(attempt);
- E->get() = attempt;
+ E.value = attempt;
// Update also in resource.
- Ref<Resource> res = E->key();
+ Ref<Resource> res = E.key;
res->set_id_for_path(local_path, attempt);
}
#else
// Make sure to start from one, as it makes format more readable.
int counter = 1;
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
- E->get() = itos(counter++);
+ for (KeyValue<RES, String> &E : external_resources) {
+ E.value = itos(counter++);
}
#endif
Vector<ResourceSort> sorted_er;
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
+ for (const KeyValue<RES, String> &E : external_resources) {
ResourceSort rs;
- rs.resource = E->key();
- rs.id = E->get();
+ rs.resource = E.key;
+ rs.id = E.value;
sorted_er.push_back(rs);
}
@@ -1849,10 +1849,16 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
if (groups.size()) {
+ // Write all groups on the same line as they're part of a section header.
+ // This improves readability while not impacting VCS friendliness too much,
+ // since it's rare to have more than 5 groups assigned to a single node.
groups.sort_custom<StringName::AlphCompare>();
- String sgroups = " groups=[\n";
+ String sgroups = " groups=[";
for (int j = 0; j < groups.size(); j++) {
- sgroups += "\"" + String(groups[j]).c_escape() + "\",\n";
+ sgroups += "\"" + String(groups[j]).c_escape() + "\"";
+ if (j < groups.size() - 1) {
+ sgroups += ", ";
+ }
}
sgroups += "]";
header += sgroups;
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 44d524f142..242e20f3b0 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -116,8 +116,8 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param) cons
}
void Shader::get_default_texture_param_list(List<StringName> *r_textures) const {
- for (const Map<StringName, Ref<Texture2D>>::Element *E = default_textures.front(); E; E = E->next()) {
- r_textures->push_back(E->key());
+ for (const KeyValue<StringName, Ref<Texture2D>> &E : default_textures) {
+ r_textures->push_back(E.key);
}
}
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index e533fb054a..7ac40b497d 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa
p_max_bound = Math_TAU + p_max_bound;
}
if (p_min_bound > p_max_bound) {
- float tmp = p_min_bound;
- p_min_bound = p_max_bound;
- p_max_bound = tmp;
+ SWAP(p_min_bound, p_max_bound);
}
+ bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
+ bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
+
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if (p_invert == false) {
- if (p_angle < p_min_bound || p_angle > p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
- } else {
- if (p_angle > p_min_bound && p_angle < p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+ if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
}
}
+
return p_angle;
}
@@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
arc_angle_max = (Math_PI * 2) + arc_angle_max;
}
if (arc_angle_min > arc_angle_max) {
- float tmp = arc_angle_min;
- arc_angle_min = arc_angle_max;
- arc_angle_max = tmp;
+ SWAP(arc_angle_min, arc_angle_max);
}
arc_angle_min += p_operation_bone->get_bone_angle();
arc_angle_max += p_operation_bone->get_bone_angle();
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
index 6e9429034f..3b5c555f89 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -247,7 +247,7 @@ void SkeletonModification2DFABRIK::chain_backwards() {
}
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
- float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length();
+ float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin()));
Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length);
current_pose.set_origin(finish_position);
@@ -268,7 +268,7 @@ void SkeletonModification2DFABRIK::chain_forwards() {
Transform2D next_pose = fabrik_transform_chain[i + 1];
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
- float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length();
+ float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin()));
Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length);
current_pose.set_origin(finish_position);
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
index 88d80a501f..4f752896a9 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -144,7 +144,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) {
// With modifications by TwistedTwigleg
Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position();
float joint_one_to_target = target_difference.length();
- float angle_atan = Math::atan2(target_difference.y, target_difference.x);
+ float angle_atan = target_difference.angle();
float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y);
float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y);
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
index ee02ede2d5..b476952d86 100644
--- a/scene/resources/skeleton_modification_3d.cpp
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -72,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r
p_max_bound = Math_TAU + p_max_bound;
}
if (p_min_bound > p_max_bound) {
- real_t tmp = p_min_bound;
- p_min_bound = p_max_bound;
- p_max_bound = tmp;
+ SWAP(p_min_bound, p_max_bound);
}
+ bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
+ bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
+
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if (p_invert == false) {
- if (p_angle < p_min_bound || p_angle > p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
- } else {
- if (p_angle > p_min_bound && p_angle < p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
+ if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
}
}
+
return p_angle;
}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
index 69f75eb7b5..dedea3e282 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -149,6 +149,11 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
return;
}
+ // Make sure the transform cache is the correct size
+ if (fabrik_transforms.size() != fabrik_data_chain.size()) {
+ fabrik_transforms.resize(fabrik_data_chain.size());
+ }
+
// Verify that all joints have a valid bone ID, and that all bone lengths are zero or more
// Also, while we are here, apply magnet positions.
for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
@@ -162,27 +167,24 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
if (_print_execution_error(fabrik_data_chain[i].length < 0, "FABRIK Joint " + itos(i) + " has an invalid joint length. Cannot execute!")) {
return;
}
-
- Transform3D local_pose_override = stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[i].bone_idx);
+ fabrik_transforms[i] = stack->skeleton->get_bone_global_pose(fabrik_data_chain[i].bone_idx);
// Apply magnet positions:
if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) {
int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx);
- Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx));
- local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
+ Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx));
+ fabrik_transforms[i].origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
} else {
- local_pose_override.origin += fabrik_data_chain[i].magnet_position;
+ fabrik_transforms[i].origin += fabrik_data_chain[i].magnet_position;
}
-
- stack->skeleton->set_bone_local_pose_override(fabrik_data_chain[i].bone_idx, local_pose_override, stack->strength, true);
}
+ Transform3D origin_global_pose_trans = stack->skeleton->get_bone_global_pose_no_override(fabrik_data_chain[0].bone_idx);
target_global_pose = stack->skeleton->world_transform_to_global_pose(node_target->get_global_transform());
- origin_global_pose = stack->skeleton->local_pose_to_global_pose(
- fabrik_data_chain[0].bone_idx, stack->skeleton->get_bone_local_pose_override(fabrik_data_chain[0].bone_idx));
+ origin_global_pose = origin_global_pose_trans;
final_joint_idx = fabrik_data_chain.size() - 1;
- real_t target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+ real_t target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin);
chain_iterations = 0;
while (target_distance > chain_tolerance) {
@@ -190,7 +192,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
chain_forwards();
// update the target distance
- target_distance = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[final_joint_idx].bone_idx, target_global_pose).origin.length();
+ target_distance = fabrik_transforms[final_joint_idx].origin.distance_to(target_global_pose.origin);
// update chain iterations
chain_iterations += 1;
@@ -205,7 +207,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
void SkeletonModification3DFABRIK::chain_backwards() {
int final_bone_idx = fabrik_data_chain[final_joint_idx].bone_idx;
- Transform3D final_joint_trans = stack->skeleton->local_pose_to_global_pose(final_bone_idx, stack->skeleton->get_bone_local_pose_override(final_bone_idx));
+ Transform3D final_joint_trans = fabrik_transforms[final_joint_idx];
// Get the direction the final bone is facing in.
stack->skeleton->update_bone_rest_forward_vector(final_bone_idx);
@@ -220,52 +222,46 @@ void SkeletonModification3DFABRIK::chain_backwards() {
// set the position of the final joint to the target position
final_joint_trans.origin = target_global_pose.origin - (direction * fabrik_data_chain[final_joint_idx].length);
- final_joint_trans = stack->skeleton->global_pose_to_local_pose(final_bone_idx, final_joint_trans);
- stack->skeleton->set_bone_local_pose_override(final_bone_idx, final_joint_trans, stack->strength, true);
+ fabrik_transforms[final_joint_idx] = final_joint_trans;
// for all other joints, move them towards the target
int i = final_joint_idx;
while (i >= 1) {
- int next_bone_idx = fabrik_data_chain[i].bone_idx;
- Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+ Transform3D next_bone_trans = fabrik_transforms[i];
i -= 1;
- int current_bone_idx = fabrik_data_chain[i].bone_idx;
- Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
+ Transform3D current_trans = fabrik_transforms[i];
- real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length();
+ real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin));
current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length);
- // Apply it back to the skeleton
- stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true);
+ // Save the result
+ fabrik_transforms[i] = current_trans;
}
}
void SkeletonModification3DFABRIK::chain_forwards() {
// Set root at the initial position.
- int origin_bone_idx = fabrik_data_chain[0].bone_idx;
- Transform3D root_transform = stack->skeleton->local_pose_to_global_pose(origin_bone_idx, stack->skeleton->get_bone_local_pose_override(origin_bone_idx));
+ Transform3D root_transform = fabrik_transforms[0];
+
root_transform.origin = origin_global_pose.origin;
- stack->skeleton->set_bone_local_pose_override(origin_bone_idx, stack->skeleton->global_pose_to_local_pose(origin_bone_idx, root_transform), stack->strength, true);
+ fabrik_transforms[0] = origin_global_pose;
for (uint32_t i = 0; i < fabrik_data_chain.size() - 1; i++) {
- int current_bone_idx = fabrik_data_chain[i].bone_idx;
- Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
- int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
- Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+ Transform3D current_trans = fabrik_transforms[i];
+ Transform3D next_bone_trans = fabrik_transforms[i + 1];
- real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length();
+ real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin));
next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length);
- // Apply it back to the skeleton
- stack->skeleton->set_bone_local_pose_override(next_bone_idx, stack->skeleton->global_pose_to_local_pose(next_bone_idx, next_bone_trans), stack->strength, true);
+ // Save the result
+ fabrik_transforms[i + 1] = next_bone_trans;
}
}
void SkeletonModification3DFABRIK::chain_apply() {
for (uint32_t i = 0; i < fabrik_data_chain.size(); i++) {
int current_bone_idx = fabrik_data_chain[i].bone_idx;
- Transform3D current_trans = stack->skeleton->get_bone_local_pose_override(current_bone_idx);
- current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, current_trans);
+ Transform3D current_trans = fabrik_transforms[i];
// If this is the last bone in the chain...
if (i == fabrik_data_chain.size() - 1) {
@@ -280,8 +276,7 @@ void SkeletonModification3DFABRIK::chain_apply() {
current_trans.basis = target_global_pose.basis.orthonormalized().scaled(current_trans.basis.get_scale());
}
} else { // every other bone in the chain...
- int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
- Transform3D next_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
+ Transform3D next_trans = fabrik_transforms[i + 1];
// Get the forward direction that the basis is facing in right now.
stack->skeleton->update_bone_rest_forward_vector(current_bone_idx);
@@ -290,9 +285,7 @@ void SkeletonModification3DFABRIK::chain_apply() {
current_trans.basis.rotate_to_align(forward_vector, current_trans.origin.direction_to(next_trans.origin));
current_trans.basis.rotate_local(forward_vector, fabrik_data_chain[i].roll);
}
- current_trans = stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans);
- current_trans.origin = Vector3(0, 0, 0);
- stack->skeleton->set_bone_local_pose_override(current_bone_idx, current_trans, stack->strength, true);
+ stack->skeleton->set_bone_local_pose_override(current_bone_idx, stack->skeleton->global_pose_to_local_pose(current_bone_idx, current_trans), stack->strength, true);
}
// Update all the bones so the next modification has up-to-date data.
@@ -374,6 +367,7 @@ int SkeletonModification3DFABRIK::get_fabrik_data_chain_length() {
void SkeletonModification3DFABRIK::set_fabrik_data_chain_length(int p_length) {
ERR_FAIL_COND(p_length < 0);
fabrik_data_chain.resize(p_length);
+ fabrik_transforms.resize(p_length);
execution_error_found = false;
notify_property_list_changed();
}
@@ -513,8 +507,11 @@ void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_join
Transform3D node_trans = tip_node->get_global_transform();
node_trans = stack->skeleton->world_transform_to_global_pose(node_trans);
- node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans);
- fabrik_data_chain[p_joint_idx].length = node_trans.origin.length();
+ //node_trans = stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, node_trans);
+ //fabrik_data_chain[p_joint_idx].length = node_trans.origin.length();
+
+ fabrik_data_chain[p_joint_idx].length = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx).origin.distance_to(node_trans.origin);
+
} else { // Use child bone(s) to update joint length, if possible
Vector<int> bone_children = stack->skeleton->get_bone_children(fabrik_data_chain[p_joint_idx].bone_idx);
if (bone_children.size() <= 0) {
@@ -522,10 +519,13 @@ void SkeletonModification3DFABRIK::fabrik_joint_auto_calculate_length(int p_join
return;
}
+ Transform3D bone_trans = stack->skeleton->get_bone_global_pose(fabrik_data_chain[p_joint_idx].bone_idx);
+
real_t final_length = 0;
for (int i = 0; i < bone_children.size(); i++) {
Transform3D child_transform = stack->skeleton->get_bone_global_pose(bone_children[i]);
- final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length();
+ final_length += bone_trans.origin.distance_to(child_transform.origin);
+ //final_length += stack->skeleton->global_pose_to_local_pose(fabrik_data_chain[p_joint_idx].bone_idx, child_transform).origin.length();
}
fabrik_data_chain[p_joint_idx].length = final_length / bone_children.size();
}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
index 9b5da883d4..6c58b8a07a 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.h
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -55,6 +55,8 @@ private:
};
LocalVector<FABRIK_Joint_Data> fabrik_data_chain;
+ LocalVector<Transform3D> fabrik_transforms;
+
NodePath target_node;
ObjectID target_node_cache;
diff --git a/scene/resources/skeleton_modification_3d_jiggle.cpp b/scene/resources/skeleton_modification_3d_jiggle.cpp
index 1fb7dad2ad..a6bcb0176a 100644
--- a/scene/resources/skeleton_modification_3d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_3d_jiggle.cpp
@@ -172,7 +172,12 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D
return;
}
- Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx));
+ Transform3D bone_local_pos = stack->skeleton->get_bone_local_pose_override(jiggle_data_chain[p_joint_idx].bone_idx);
+ if (bone_local_pos == Transform3D()) {
+ bone_local_pos = stack->skeleton->get_bone_pose(jiggle_data_chain[p_joint_idx].bone_idx);
+ }
+
+ Transform3D new_bone_trans = stack->skeleton->local_pose_to_global_pose(jiggle_data_chain[p_joint_idx].bone_idx, bone_local_pos);
Vector3 target_position = stack->skeleton->world_transform_to_global_pose(p_target->get_global_transform()).origin;
jiggle_data_chain[p_joint_idx].force = (target_position - jiggle_data_chain[p_joint_idx].dynamic_position) * jiggle_data_chain[p_joint_idx].stiffness * p_delta;
diff --git a/scene/resources/skeleton_modification_3d_lookat.cpp b/scene/resources/skeleton_modification_3d_lookat.cpp
index afdb077e71..f3b0f41d60 100644
--- a/scene/resources/skeleton_modification_3d_lookat.cpp
+++ b/scene/resources/skeleton_modification_3d_lookat.cpp
@@ -96,8 +96,10 @@ void SkeletonModification3DLookAt::_execute(real_t p_delta) {
if (_print_execution_error(bone_idx <= -1, "Bone index is invalid. Cannot execute modification!")) {
return;
}
-
Transform3D new_bone_trans = stack->skeleton->get_bone_local_pose_override(bone_idx);
+ if (new_bone_trans == Transform3D()) {
+ new_bone_trans = stack->skeleton->get_bone_pose(bone_idx);
+ }
Vector3 target_pos = stack->skeleton->global_pose_to_local_pose(bone_idx, stack->skeleton->world_transform_to_global_pose(target->get_global_transform())).origin;
// Lock the rotation to a plane relative to the bone by changing the target position
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
index ae7a3bab7e..93ec155a88 100644
--- a/scene/resources/skeleton_modification_3d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -178,7 +178,16 @@ void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
}
Transform3D pole_trans = stack->skeleton->world_transform_to_global_pose(pole->get_global_transform());
- bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
+ Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx);
+ if (bone_one_local_pos == Transform3D()) {
+ bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx);
+ }
+ Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx);
+ if (bone_two_local_pos == Transform3D()) {
+ bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx);
+ }
+
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos);
bone_one_trans = bone_one_trans.looking_at(pole_trans.origin, Vector3(0, 1, 0));
bone_one_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_one_bone_idx, bone_one_trans.basis);
stack->skeleton->update_bone_rest_forward_vector(joint_one_bone_idx);
@@ -186,7 +195,7 @@ void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
stack->skeleton->set_bone_local_pose_override(joint_one_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_one_bone_idx, bone_one_trans), stack->strength, true);
stack->skeleton->force_update_bone_children_transforms(joint_one_bone_idx);
- bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos);
bone_two_trans = bone_two_trans.looking_at(target_trans.origin, Vector3(0, 1, 0));
bone_two_trans.basis = stack->skeleton->global_pose_z_forward_to_bone_forward(joint_two_bone_idx, bone_two_trans.basis);
stack->skeleton->update_bone_rest_forward_vector(joint_two_bone_idx);
@@ -194,8 +203,17 @@ void SkeletonModification3DTwoBoneIK::_execute(real_t p_delta) {
stack->skeleton->set_bone_local_pose_override(joint_two_bone_idx, stack->skeleton->global_pose_to_local_pose(joint_two_bone_idx, bone_two_trans), stack->strength, true);
stack->skeleton->force_update_bone_children_transforms(joint_two_bone_idx);
} else {
- bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx));
- bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx));
+ Transform3D bone_one_local_pos = stack->skeleton->get_bone_local_pose_override(joint_one_bone_idx);
+ if (bone_one_local_pos == Transform3D()) {
+ bone_one_local_pos = stack->skeleton->get_bone_pose(joint_one_bone_idx);
+ }
+ Transform3D bone_two_local_pos = stack->skeleton->get_bone_local_pose_override(joint_two_bone_idx);
+ if (bone_two_local_pos == Transform3D()) {
+ bone_two_local_pos = stack->skeleton->get_bone_pose(joint_two_bone_idx);
+ }
+
+ bone_one_trans = stack->skeleton->local_pose_to_global_pose(joint_one_bone_idx, bone_one_local_pos);
+ bone_two_trans = stack->skeleton->local_pose_to_global_pose(joint_two_bone_idx, bone_two_local_pos);
}
Transform3D bone_two_tip_trans;
@@ -455,7 +473,7 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
joint_two_length = 0;
for (int i = 0; i < bone_two_children.size(); i++) {
joint_two_length += bone_two_rest_trans.origin.distance_to(
- stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin);
+ stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin);
}
joint_two_length = joint_two_length / bone_two_children.size();
} else {
diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp
index 72c1c330ef..db9fe62b4d 100644
--- a/scene/resources/skeleton_modification_stack_2d.cpp
+++ b/scene/resources/skeleton_modification_stack_2d.cpp
@@ -247,7 +247,7 @@ void SkeletonModificationStack2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification);
ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification);
- ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack2D::set_modification_count);
ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count);
ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup);
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
index 3fce0e5dbd..a724b732b9 100644
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -200,7 +200,7 @@ void SkeletonModificationStack3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification);
ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification);
- ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack3D::set_modification_count);
ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count);
ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup);
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 39082b6f7a..de94c92cac 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -125,10 +125,6 @@ float ProceduralSkyMaterial::get_sun_curve() const {
return sun_curve;
}
-bool ProceduralSkyMaterial::_can_do_next_pass() const {
- return false;
-}
-
Shader::Mode ProceduralSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -179,14 +175,14 @@ void ProceduralSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve);
ADD_GROUP("Sky", "sky_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color"), "set_sky_top_color", "get_sky_top_color");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color"), "set_sky_horizon_color", "get_sky_horizon_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy");
ADD_GROUP("Ground", "ground_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color"), "set_ground_bottom_color", "get_ground_bottom_color");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color"), "set_ground_horizon_color", "get_ground_horizon_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy");
@@ -312,10 +308,6 @@ Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const {
return panorama;
}
-bool PanoramaSkyMaterial::_can_do_next_pass() const {
- return false;
-}
-
Shader::Mode PanoramaSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -482,10 +474,6 @@ Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const {
return night_sky;
}
-bool PhysicalSkyMaterial::_can_do_next_pass() const {
- return false;
-}
-
Shader::Mode PhysicalSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -543,16 +531,16 @@ void PhysicalSkyMaterial::_bind_methods() {
ADD_GROUP("Rayleigh", "rayleigh_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rayleigh_coefficient", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_rayleigh_coefficient", "get_rayleigh_coefficient");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color"), "set_rayleigh_color", "get_rayleigh_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_rayleigh_color", "get_rayleigh_color");
ADD_GROUP("Mie", "mie_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_coefficient", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_mie_coefficient", "get_mie_coefficient");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_eccentricity", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_mie_eccentricity", "get_mie_eccentricity");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color"), "set_mie_color", "get_mie_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_mie_color", "get_mie_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color"), "set_ground_color", "get_ground_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky");
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index 63e730617b..daeda212d4 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -58,7 +58,6 @@ private:
protected:
static void _bind_methods();
- virtual bool _can_do_next_pass() const override;
public:
void set_sky_top_color(const Color &p_sky_top);
@@ -117,7 +116,6 @@ private:
protected:
static void _bind_methods();
- virtual bool _can_do_next_pass() const override;
public:
void set_panorama(const Ref<Texture2D> &p_panorama);
@@ -159,7 +157,6 @@ private:
protected:
static void _bind_methods();
- virtual bool _can_do_next_pass() const override;
public:
void set_rayleigh_coefficient(float p_rayleigh);
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
index 140c6f821f..01afb00283 100644
--- a/scene/resources/sprite_frames.cpp
+++ b/scene/resources/sprite_frames.cpp
@@ -108,15 +108,15 @@ Vector<String> SpriteFrames::_get_animation_list() const {
}
void SpriteFrames::get_animation_list(List<StringName> *r_animations) const {
- for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
- r_animations->push_back(E->key());
+ for (const KeyValue<StringName, Anim> &E : animations) {
+ r_animations->push_back(E.key);
}
}
Vector<String> SpriteFrames::get_animation_names() const {
Vector<String> names;
- for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
- names.push_back(E->key());
+ for (const KeyValue<StringName, Anim> &E : animations) {
+ names.push_back(E.key);
}
names.sort();
return names;
@@ -164,14 +164,14 @@ Array SpriteFrames::_get_frames() const {
Array SpriteFrames::_get_animations() const {
Array anims;
- for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Anim> &E : animations) {
Dictionary d;
- d["name"] = E->key();
- d["speed"] = E->get().speed;
- d["loop"] = E->get().loop;
+ d["name"] = E.key;
+ d["speed"] = E.value.speed;
+ d["loop"] = E.value.loop;
Array frames;
- for (int i = 0; i < E->get().frames.size(); i++) {
- frames.push_back(E->get().frames[i]);
+ for (int i = 0; i < E.value.frames.size(); i++) {
+ frames.push_back(E.value.frames[i]);
}
d["frames"] = frames;
anims.push_back(d);
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 875aa30824..455af8a40c 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -409,7 +409,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 4 + 0] = CLAMP(int32_t(c.r * 255.0), 0, 255);
w[idx * 4 + 1] = CLAMP(int32_t(c.g * 255.0), 0, 255);
w[idx * 4 + 2] = CLAMP(int32_t(c.b * 255.0), 0, 255);
@@ -426,7 +426,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 4 + 0] = uint8_t(int8_t(CLAMP(int32_t(c.r * 127.0), -128, 127)));
w[idx * 4 + 1] = uint8_t(int8_t(CLAMP(int32_t(c.g * 127.0), -128, 127)));
w[idx * 4 + 2] = uint8_t(int8_t(CLAMP(int32_t(c.b * 127.0), -128, 127)));
@@ -443,7 +443,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 2 + 0] = Math::make_half_float(c.r);
w[idx * 2 + 1] = Math::make_half_float(c.g);
}
@@ -458,7 +458,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 4 + 0] = Math::make_half_float(c.r);
w[idx * 4 + 1] = Math::make_half_float(c.g);
w[idx * 4 + 2] = Math::make_half_float(c.b);
@@ -475,7 +475,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx] = c.r;
}
@@ -489,7 +489,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 2 + 0] = c.r;
w[idx * 2 + 1] = c.g;
}
@@ -504,7 +504,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 3 + 0] = c.r;
w[idx * 3 + 1] = c.g;
w[idx * 3 + 2] = c.b;
@@ -520,7 +520,7 @@ Array SurfaceTool::commit_to_arrays() {
for (uint32_t idx = 0; idx < vertex_array.size(); idx++) {
const Vertex &v = vertex_array[idx];
- const Color &c = v.custom[idx];
+ const Color &c = v.custom[fmt];
w[idx * 4 + 0] = c.r;
w[idx * 4 + 1] = c.g;
w[idx * 4 + 2] = c.b;
@@ -679,6 +679,9 @@ void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, Local
_create_list_from_arrays(arr, r_vertex, r_index, lformat);
}
+static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
+static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
+
void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint32_t *r_format) {
ret.clear();
@@ -733,8 +736,6 @@ void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays
if (warr.size()) {
lformat |= RS::ARRAY_FORMAT_WEIGHTS;
}
- static const uint32_t custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
- static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) {
ERR_CONTINUE_MSG(p_arrays[RS::ARRAY_CUSTOM0 + i].get_type() == Variant::PACKED_BYTE_ARRAY, "Extracting Byte/Half formats is not supported");
@@ -832,6 +833,12 @@ void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) {
clear();
primitive = Mesh::PRIMITIVE_TRIANGLES;
_create_list_from_arrays(p_arrays, &vertex_array, &index_array, format);
+
+ for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
+ if (format & custom_mask[j]) {
+ last_custom_format[j] = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK);
+ }
+ }
}
void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
@@ -841,6 +848,12 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
primitive = p_existing->surface_get_primitive_type(p_surface);
_create_list(p_existing, p_surface, &vertex_array, &index_array, format);
material = p_existing->surface_get_material(p_surface);
+
+ for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
+ if (format & custom_mask[j]) {
+ last_custom_format[j] = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK);
+ }
+ }
}
void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name) {
@@ -863,6 +876,12 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur
Array mesh = arr[shape_idx];
ERR_FAIL_COND(mesh.size() != RS::ARRAY_MAX);
_create_list_from_arrays(arr[shape_idx], &vertex_array, &index_array, format);
+
+ for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
+ if (format & custom_mask[j]) {
+ last_custom_format[j] = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK);
+ }
+ }
}
void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform) {
@@ -878,6 +897,16 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
LocalVector<int> nindices;
_create_list(p_existing, p_surface, &nvertices, &nindices, nformat);
format |= nformat;
+
+ for (int j = 0; j < RS::ARRAY_CUSTOM_COUNT; j++) {
+ if (format & custom_mask[j]) {
+ CustomFormat new_format = (CustomFormat)((format >> custom_shift[j]) & RS::ARRAY_FORMAT_CUSTOM_MASK);
+ if (last_custom_format[j] != CUSTOM_MAX && last_custom_format[j] != new_format) {
+ WARN_PRINT(vformat("Custom %d format %d mismatch when appending format %d", j, last_custom_format[j], new_format));
+ }
+ last_custom_format[j] = new_format;
+ }
+ }
int vfrom = vertex_array.size();
for (uint32_t vi = 0; vi < nvertices.size(); vi++) {
@@ -1145,8 +1174,11 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun
Vector<int> lod;
ERR_FAIL_COND_V(simplify_func == nullptr, lod);
+ ERR_FAIL_COND_V(p_target_index_count < 0, lod);
ERR_FAIL_COND_V(vertex_array.size() == 0, lod);
ERR_FAIL_COND_V(index_array.size() == 0, lod);
+ ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod);
+ ERR_FAIL_COND_V(index_array.size() < (unsigned int)p_target_index_count, lod);
lod.resize(index_array.size());
LocalVector<float> vertices; //uses floats
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index d2f38ba836..0094518967 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -53,7 +53,7 @@ void TextLine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
- ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override);
+ ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override);
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
@@ -112,7 +112,7 @@ void TextLine::_shape() {
TS->shaped_text_tab_align(rid, tab_stops);
}
- uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
if (overrun_behavior != OVERRUN_NO_TRIMMING) {
switch (overrun_behavior) {
case OVERRUN_TRIM_WORD_ELLIPSIS:
@@ -195,15 +195,7 @@ TextServer::Orientation TextLine::get_orientation() const {
return TS->shaped_text_get_orientation(rid);
}
-void TextLine::_set_bidi_override(const Array &p_override) {
- Vector<Vector2i> overrides;
- for (int i = 0; i < p_override.size(); i++) {
- overrides.push_back(p_override[i]);
- }
- set_bidi_override(overrides);
-}
-
-void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
+void TextLine::set_bidi_override(const Array &p_override) {
TS->shaped_text_set_bidi_override(rid, p_override);
dirty = true;
}
@@ -256,14 +248,14 @@ void TextLine::tab_align(const Vector<float> &p_tab_stops) {
dirty = true;
}
-void TextLine::set_flags(uint8_t p_flags) {
+void TextLine::set_flags(uint16_t p_flags) {
if (flags != p_flags) {
flags = p_flags;
dirty = true;
}
}
-uint8_t TextLine::get_flags() const {
+uint16_t TextLine::get_flags() const {
return flags;
}
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index 9ed9c2f177..43739f27ec 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -56,7 +56,7 @@ private:
bool dirty = true;
float width = -1.0;
- uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
HAlign align = HALIGN_LEFT;
OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS;
@@ -75,7 +75,7 @@ public:
void set_direction(TextServer::Direction p_direction);
TextServer::Direction get_direction() const;
- void set_bidi_override(const Vector<Vector2i> &p_override);
+ void set_bidi_override(const Array &p_override);
void set_orientation(TextServer::Orientation p_orientation);
TextServer::Orientation get_orientation() const;
@@ -95,8 +95,8 @@ public:
void tab_align(const Vector<float> &p_tab_stops);
- void set_flags(uint8_t p_flags);
- uint8_t get_flags() const;
+ void set_flags(uint16_t p_flags);
+ uint16_t get_flags() const;
void set_text_overrun_behavior(OverrunBehavior p_behavior);
OverrunBehavior get_text_overrun_behavior() const;
@@ -120,8 +120,6 @@ public:
int hit_test(float p_coords) const;
- void _set_bidi_override(const Array &p_override);
-
TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
TextLine();
~TextLine();
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 62949b9b98..fae1de94d3 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -53,7 +53,7 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
- ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override);
+ ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override);
ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
@@ -84,7 +84,7 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
- ClassDB::bind_method(D_METHOD("get_non_wraped_size"), &TextParagraph::get_non_wraped_size);
+ ClassDB::bind_method(D_METHOD("get_non_wrapped_size"), &TextParagraph::get_non_wrapped_size);
ClassDB::bind_method(D_METHOD("get_size"), &TextParagraph::get_size);
ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid);
@@ -158,9 +158,9 @@ void TextParagraph::_shape_lines() {
if (h_offset > 0) {
// Dropcap, flow around.
- Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags);
- for (int i = 0; i < line_breaks.size(); i++) {
- RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags);
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
if (v_offset < h) {
TS->free(line);
@@ -171,21 +171,21 @@ void TextParagraph::_shape_lines() {
}
dropcap_lines++;
v_offset -= h;
- start = line_breaks[i].y;
+ start = line_breaks[i + 1];
lines_rid.push_back(line);
}
}
// Use fixed for the rest of lines.
- Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags);
- for (int i = 0; i < line_breaks.size(); i++) {
- RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags);
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(line, tab_stops);
}
lines_rid.push_back(line);
}
- uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
if (overrun_behavior != OVERRUN_NO_TRIMMING) {
switch (overrun_behavior) {
case OVERRUN_TRIM_WORD_ELLIPSIS:
@@ -347,15 +347,7 @@ int TextParagraph::get_spacing_bottom() const {
return spacing_bottom;
}
-void TextParagraph::_set_bidi_override(const Array &p_override) {
- Vector<Vector2i> overrides;
- for (int i = 0; i < p_override.size(); i++) {
- overrides.push_back(p_override[i]);
- }
- set_bidi_override(overrides);
-}
-
-void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) {
+void TextParagraph::set_bidi_override(const Array &p_override) {
TS->shaped_text_set_bidi_override(rid, p_override);
lines_dirty = true;
}
@@ -392,14 +384,14 @@ void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
lines_dirty = true;
}
-void TextParagraph::set_flags(uint8_t p_flags) {
+void TextParagraph::set_flags(uint16_t p_flags) {
if (flags != p_flags) {
flags = p_flags;
lines_dirty = true;
}
}
-uint8_t TextParagraph::get_flags() const {
+uint16_t TextParagraph::get_flags() const {
return flags;
}
@@ -425,7 +417,7 @@ float TextParagraph::get_width() const {
return width;
}
-Size2 TextParagraph::get_non_wraped_size() const {
+Size2 TextParagraph::get_non_wrapped_size() const {
const_cast<TextParagraph *>(this)->_shape_lines();
if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom);
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index ee7bbab9c5..701c9a17cd 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -63,7 +63,7 @@ private:
float width = -1.0;
int max_lines_visible = -1;
- uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING;
HAlign align = HALIGN_LEFT;
@@ -94,7 +94,7 @@ public:
void set_preserve_control(bool p_enabled);
bool get_preserve_control() const;
- void set_bidi_override(const Vector<Vector2i> &p_override);
+ void set_bidi_override(const Array &p_override);
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
@@ -108,8 +108,8 @@ public:
void tab_align(const Vector<float> &p_tab_stops);
- void set_flags(uint8_t p_flags);
- uint8_t get_flags() const;
+ void set_flags(uint16_t p_flags);
+ uint16_t get_flags() const;
void set_text_overrun_behavior(OverrunBehavior p_behavior);
OverrunBehavior get_text_overrun_behavior() const;
@@ -120,7 +120,7 @@ public:
void set_max_lines_visible(int p_lines);
int get_max_lines_visible() const;
- Size2 get_non_wraped_size() const;
+ Size2 get_non_wrapped_size() const;
Size2 get_size() const;
@@ -153,8 +153,6 @@ public:
int hit_test(const Point2 &p_coords) const;
- void _set_bidi_override(const Array &p_override);
-
TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
TextParagraph();
~TextParagraph();
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 063a13efc0..1b3a7a5f2a 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -131,25 +131,6 @@ void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, ""));
}
-void ImageTexture::_reload_hook(const RID &p_hook) {
- String path = get_path();
- if (!path.is_resource_file()) {
- return;
- }
-
- Ref<Image> img;
- img.instantiate();
- Error err = ImageLoader::load_image(path, img);
-
- ERR_FAIL_COND_MSG(err != OK, "Cannot load image from path '" + path + "'.");
-
- RID new_texture = RenderingServer::get_singleton()->texture_2d_create(img);
- RenderingServer::get_singleton()->texture_replace(texture, new_texture);
-
- notify_property_list_changed();
- emit_changed();
-}
-
void ImageTexture::create_from_image(const Ref<Image> &p_image) {
ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image");
w = p_image->get_width();
@@ -192,10 +173,6 @@ void ImageTexture::update(const Ref<Image> &p_image) {
image_stored = true;
}
-void ImageTexture::_resource_path_changed() {
- String path = get_path();
-}
-
Ref<Image> ImageTexture::get_image() const {
if (image_stored) {
return RenderingServer::get_singleton()->texture_2d_get(texture);
@@ -303,7 +280,6 @@ void ImageTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update);
ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override);
- ClassDB::bind_method(D_METHOD("_reload_hook", "rid"), &ImageTexture::_reload_hook);
}
ImageTexture::ImageTexture() {}
@@ -1272,6 +1248,14 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
return atlas->is_pixel_opaque(x, y);
}
+Ref<Image> AtlasTexture::get_image() const {
+ if (!atlas.is_valid() || !atlas->get_image().is_valid()) {
+ return Ref<Image>();
+ }
+
+ return atlas->get_image()->get_rect(region);
+}
+
AtlasTexture::AtlasTexture() {}
/////////////////////////////////////////
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index f6b991c335..862b9a47a6 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -98,8 +98,6 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- void _reload_hook(const RID &p_hook);
- virtual void _resource_path_changed() override;
static void _bind_methods();
public:
@@ -252,6 +250,8 @@ public:
bool is_pixel_opaque(int p_x, int p_y) const override;
+ virtual Ref<Image> get_image() const override;
+
AtlasTexture();
};
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index e49d883ba4..65cdc1e24e 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -29,270 +29,20 @@
/*************************************************************************/
#include "theme.h"
-#include "core/io/file_access.h"
#include "core/string/print_string.h"
-void Theme::_emit_theme_changed() {
- if (no_change_propagation) {
- return;
- }
-
- notify_property_list_changed();
- emit_changed();
-}
-
-Vector<String> Theme::_get_icon_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_icon_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_icon_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_icon_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_stylebox_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_stylebox_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_stylebox_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_size_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_size_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_size_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_color_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_color_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_color_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_color_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_constant_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_constant_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_constant_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_constant_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return _get_color_list(p_theme_type);
- case DATA_TYPE_CONSTANT:
- return _get_constant_list(p_theme_type);
- case DATA_TYPE_FONT:
- return _get_font_list(p_theme_type);
- case DATA_TYPE_FONT_SIZE:
- return _get_font_size_list(p_theme_type);
- case DATA_TYPE_ICON:
- return _get_icon_list(p_theme_type);
- case DATA_TYPE_STYLEBOX:
- return _get_stylebox_list(p_theme_type);
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return Vector<String>();
-}
-
-Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return _get_color_type_list();
- case DATA_TYPE_CONSTANT:
- return _get_constant_type_list();
- case DATA_TYPE_FONT:
- return _get_font_type_list();
- case DATA_TYPE_FONT_SIZE:
- return _get_font_size_type_list();
- case DATA_TYPE_ICON:
- return _get_icon_type_list();
- case DATA_TYPE_STYLEBOX:
- return _get_stylebox_type_list();
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return Vector<String>();
-}
-
-Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_type_variation_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_type_list(&il);
- ilret.resize(il.size());
+// Universal Theme resources used when no other theme has the item.
+Ref<Theme> Theme::default_theme;
+Ref<Theme> Theme::project_default_theme;
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
+// Universal default values, final fallback for every theme.
+float Theme::default_base_scale = 1.0;
+Ref<Texture2D> Theme::default_icon;
+Ref<StyleBox> Theme::default_style;
+Ref<Font> Theme::default_font;
+int Theme::default_font_size = 16;
+// Dynamic properties.
bool Theme::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
@@ -452,6 +202,63 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+// Universal fallback Theme resources.
+Ref<Theme> Theme::get_default() {
+ return default_theme;
+}
+
+void Theme::set_default(const Ref<Theme> &p_default) {
+ default_theme = p_default;
+}
+
+Ref<Theme> Theme::get_project_default() {
+ return project_default_theme;
+}
+
+void Theme::set_project_default(const Ref<Theme> &p_project_default) {
+ project_default_theme = p_project_default;
+}
+
+// Universal fallback values for theme item types.
+void Theme::set_default_base_scale(float p_base_scale) {
+ default_base_scale = p_base_scale;
+}
+
+void Theme::set_default_icon(const Ref<Texture2D> &p_icon) {
+ default_icon = p_icon;
+}
+
+void Theme::set_default_style(const Ref<StyleBox> &p_style) {
+ default_style = p_style;
+}
+
+void Theme::set_default_font(const Ref<Font> &p_font) {
+ default_font = p_font;
+}
+
+void Theme::set_default_font_size(int p_font_size) {
+ default_font_size = p_font_size;
+}
+
+// Fallback values for theme item types, configurable per theme.
+void Theme::set_default_theme_base_scale(float p_base_scale) {
+ if (default_theme_base_scale == p_base_scale) {
+ return;
+ }
+
+ default_theme_base_scale = p_base_scale;
+
+ _emit_theme_changed();
+}
+
+float Theme::get_default_theme_base_scale() const {
+ return default_theme_base_scale;
+}
+
+bool Theme::has_default_theme_base_scale() const {
+ return default_theme_base_scale > 0.0;
+}
+
void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
if (default_theme_font == p_default_font) {
return;
@@ -464,7 +271,7 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
default_theme_font = p_default_font;
if (default_theme_font.is_valid()) {
- default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed();
@@ -474,6 +281,10 @@ Ref<Font> Theme::get_default_theme_font() const {
return default_theme_font;
}
+bool Theme::has_default_theme_font() const {
+ return default_theme_font.is_valid();
+}
+
void Theme::set_default_theme_font_size(int p_font_size) {
if (default_theme_font_size == p_font_size) {
return;
@@ -488,57 +299,25 @@ int Theme::get_default_theme_font_size() const {
return default_theme_font_size;
}
-Ref<Theme> Theme::project_default_theme;
-Ref<Theme> Theme::default_theme;
-Ref<Texture2D> Theme::default_icon;
-Ref<StyleBox> Theme::default_style;
-Ref<Font> Theme::default_font;
-int Theme::default_font_size = 16;
-
-Ref<Theme> Theme::get_default() {
- return default_theme;
-}
-
-void Theme::set_default(const Ref<Theme> &p_default) {
- default_theme = p_default;
-}
-
-Ref<Theme> Theme::get_project_default() {
- return project_default_theme;
-}
-
-void Theme::set_project_default(const Ref<Theme> &p_project_default) {
- project_default_theme = p_project_default;
-}
-
-void Theme::set_default_icon(const Ref<Texture2D> &p_icon) {
- default_icon = p_icon;
-}
-
-void Theme::set_default_style(const Ref<StyleBox> &p_style) {
- default_style = p_style;
-}
-
-void Theme::set_default_font(const Ref<Font> &p_font) {
- default_font = p_font;
-}
-
-void Theme::set_default_font_size(int p_font_size) {
- default_font_size = p_font_size;
+bool Theme::has_default_theme_font_size() const {
+ return default_theme_font_size > 0;
}
+// Icons.
void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) {
+ bool existing = false;
if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
+ existing = true;
icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
icon_map[p_theme_type][p_name] = p_icon;
if (p_icon.is_valid()) {
- icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const {
@@ -565,7 +344,7 @@ void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name,
icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name];
icon_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) {
@@ -578,7 +357,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type)
icon_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -611,18 +390,21 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const {
}
}
+// Styleboxes.
void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) {
+ bool existing = false;
if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
+ existing = true;
style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
style_map[p_theme_type][p_name] = p_style;
if (p_style.is_valid()) {
- style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
@@ -649,7 +431,7 @@ void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_na
style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name];
style_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) {
@@ -662,7 +444,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t
style_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -695,24 +477,27 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
}
}
+// Fonts.
void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) {
+ bool existing = false;
if (font_map[p_theme_type][p_name].is_valid()) {
+ existing = true;
font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
font_map[p_theme_type][p_name] = p_font;
if (p_font.is_valid()) {
- font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
return font_map[p_theme_type][p_name];
- } else if (default_theme_font.is_valid()) {
+ } else if (has_default_theme_font()) {
return default_theme_font;
} else {
return default_font;
@@ -720,7 +505,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_ty
}
bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid());
+ return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_theme_font());
}
bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
@@ -735,7 +520,7 @@ void Theme::rename_font(const StringName &p_old_name, const StringName &p_name,
font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name];
font_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) {
@@ -748,7 +533,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type)
font_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -781,16 +566,18 @@ void Theme::get_font_type_list(List<StringName> *p_list) const {
}
}
+// Font sizes.
void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
+ bool existing = has_font_size_nocheck(p_name, p_theme_type);
font_size_map[p_theme_type][p_name] = p_font_size;
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) {
return font_size_map[p_theme_type][p_name];
- } else if (default_theme_font_size > 0) {
+ } else if (has_default_theme_font_size()) {
return default_theme_font_size;
} else {
return default_font_size;
@@ -798,7 +585,7 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_typ
}
bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || (default_theme_font_size > 0));
+ return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_theme_font_size());
}
bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
@@ -813,7 +600,7 @@ void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_n
font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name];
font_size_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) {
@@ -822,7 +609,7 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_
font_size_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -855,10 +642,12 @@ void Theme::get_font_size_type_list(List<StringName> *p_list) const {
}
}
+// Colors.
void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
+ bool existing = has_color_nocheck(p_name, p_theme_type);
color_map[p_theme_type][p_name] = p_color;
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const {
@@ -885,7 +674,7 @@ void Theme::rename_color(const StringName &p_old_name, const StringName &p_name,
color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name];
color_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) {
@@ -894,7 +683,7 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type
color_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -927,10 +716,12 @@ void Theme::get_color_type_list(List<StringName> *p_list) const {
}
}
+// Theme constants.
void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
+ bool existing = has_constant_nocheck(p_name, p_theme_type);
constant_map[p_theme_type][p_name] = p_constant;
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const {
@@ -957,7 +748,7 @@ void Theme::rename_constant(const StringName &p_old_name, const StringName &p_na
constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name];
constant_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) {
@@ -966,7 +757,7 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t
constant_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -999,6 +790,7 @@ void Theme::get_constant_type_list(List<StringName> *p_list) const {
}
}
+// Generic methods for managing theme items.
void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) {
switch (p_data_type) {
case DATA_TYPE_COLOR: {
@@ -1230,6 +1022,7 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l
}
}
+// Theme type variations.
void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type.");
@@ -1243,7 +1036,7 @@ void Theme::set_type_variation(const StringName &p_theme_type, const StringName
variation_map[p_theme_type] = p_base_type;
variation_base_map[p_base_type].push_back(p_theme_type);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const {
@@ -1257,7 +1050,7 @@ void Theme::clear_type_variation(const StringName &p_theme_type) {
variation_base_map[base_type].erase(p_theme_type);
variation_map.erase(p_theme_type);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
StringName Theme::get_type_variation_base(const StringName &p_theme_type) const {
@@ -1287,67 +1080,355 @@ void Theme::get_type_variation_list(const StringName &p_base_type, List<StringNa
}
}
-void Theme::_freeze_change_propagation() {
- no_change_propagation = true;
-}
+// Theme types.
+void Theme::get_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
-void Theme::_unfreeze_and_propagate_changes() {
- no_change_propagation = false;
- _emit_theme_changed();
+ Set<StringName> types;
+ const StringName *key = nullptr;
+
+ // Icons.
+ while ((key = icon_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // StyleBoxes.
+ while ((key = style_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Fonts.
+ while ((key = font_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Font sizes.
+ while ((key = font_size_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Colors.
+ while ((key = color_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Constants.
+ while ((key = constant_map.next(key))) {
+ types.insert(*key);
+ }
+
+ for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
+ p_list->push_back(E->get());
+ }
}
-void Theme::clear() {
- // These items need disconnecting.
- {
- const StringName *K = nullptr;
- while ((K = icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = icon_map[*K].next(L))) {
- Ref<Texture2D> icon = icon_map[*K][*L];
- if (icon.is_valid()) {
- icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
+void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
+ ERR_FAIL_NULL(p_list);
+
+ // Build the dependency chain for type variations.
+ if (p_type_variation != StringName()) {
+ StringName variation_name = p_type_variation;
+ while (variation_name != StringName()) {
+ p_list->push_back(variation_name);
+ variation_name = get_type_variation_base(variation_name);
+
+ // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
+ if (variation_name == p_base_type) {
+ break;
}
}
}
- {
- const StringName *K = nullptr;
- while ((K = style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = style_map[*K].next(L))) {
- Ref<StyleBox> style = style_map[*K][*L];
- if (style.is_valid()) {
- style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
+ // Continue building the chain using native class hierarchy.
+ StringName class_name = p_base_type;
+ while (class_name != StringName()) {
+ p_list->push_back(class_name);
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
}
+}
- {
- const StringName *K = nullptr;
- while ((K = font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = font_map[*K].next(L))) {
- Ref<Font> font = font_map[*K][*L];
- if (font.is_valid()) {
- font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
+// Internal methods for getting lists as a Vector of String (compatible with public API).
+Vector<String> Theme::_get_icon_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_icon_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
}
+ return ilret;
+}
- icon_map.clear();
- style_map.clear();
- font_map.clear();
- font_size_map.clear();
- color_map.clear();
- constant_map.clear();
+Vector<String> Theme::_get_icon_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
- variation_map.clear();
- variation_base_map.clear();
+ get_icon_type_list(&il);
+ ilret.resize(il.size());
- _emit_theme_changed();
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_stylebox_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_size_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_size_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_size_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_color_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_color_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_constant_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_constant_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return _get_color_list(p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return _get_constant_list(p_theme_type);
+ case DATA_TYPE_FONT:
+ return _get_font_list(p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return _get_font_size_list(p_theme_type);
+ case DATA_TYPE_ICON:
+ return _get_icon_list(p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return _get_stylebox_list(p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Vector<String>();
+}
+
+Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return _get_color_type_list();
+ case DATA_TYPE_CONSTANT:
+ return _get_constant_type_list();
+ case DATA_TYPE_FONT:
+ return _get_font_type_list();
+ case DATA_TYPE_FONT_SIZE:
+ return _get_font_size_type_list();
+ case DATA_TYPE_ICON:
+ return _get_icon_type_list();
+ case DATA_TYPE_STYLEBOX:
+ return _get_stylebox_type_list();
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Vector<String>();
+}
+
+Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_type_variation_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+// Theme bulk manipulations.
+void Theme::_emit_theme_changed(bool p_notify_list_changed) {
+ if (no_change_propagation) {
+ return;
+ }
+
+ if (p_notify_list_changed) {
+ notify_property_list_changed();
+ }
+ emit_changed();
+}
+
+void Theme::_freeze_change_propagation() {
+ no_change_propagation = true;
+}
+
+void Theme::_unfreeze_and_propagate_changes() {
+ no_change_propagation = false;
+ _emit_theme_changed(true);
}
void Theme::merge_with(const Ref<Theme> &p_other) {
@@ -1434,80 +1515,58 @@ void Theme::merge_with(const Ref<Theme> &p_other) {
_unfreeze_and_propagate_changes();
}
-void Theme::get_type_list(List<StringName> *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- Set<StringName> types;
- const StringName *key = nullptr;
-
- // Icons.
- while ((key = icon_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // StyleBoxes.
- while ((key = style_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Fonts.
- while ((key = font_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Font sizes.
- while ((key = font_size_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Colors.
- while ((key = color_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Constants.
- while ((key = constant_map.next(key))) {
- types.insert(*key);
+void Theme::clear() {
+ // These items need disconnecting.
+ {
+ const StringName *K = nullptr;
+ while ((K = icon_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = icon_map[*K].next(L))) {
+ Ref<Texture2D> icon = icon_map[*K][*L];
+ if (icon.is_valid()) {
+ icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+ }
}
- for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ {
+ const StringName *K = nullptr;
+ while ((K = style_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = style_map[*K].next(L))) {
+ Ref<StyleBox> style = style_map[*K][*L];
+ if (style.is_valid()) {
+ style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+ }
}
-}
-void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
- ERR_FAIL_NULL(p_list);
-
- // Build the dependency chain for type variations.
- if (p_type_variation != StringName()) {
- StringName variation_name = p_type_variation;
- while (variation_name != StringName()) {
- p_list->push_back(variation_name);
- variation_name = get_type_variation_base(variation_name);
-
- // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
- if (variation_name == p_base_type) {
- break;
+ {
+ const StringName *K = nullptr;
+ while ((K = font_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = font_map[*K].next(L))) {
+ Ref<Font> font = font_map[*K][*L];
+ if (font.is_valid()) {
+ font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
}
}
}
- // Continue building the chain using native class hierarchy.
- StringName class_name = p_base_type;
- while (class_name != StringName()) {
- p_list->push_back(class_name);
- class_name = ClassDB::get_parent_class_nocheck(class_name);
- }
+ icon_map.clear();
+ style_map.clear();
+ font_map.clear();
+ font_size_map.clear();
+ color_map.clear();
+ constant_map.clear();
+
+ variation_map.clear();
+ variation_base_map.clear();
+
+ _emit_theme_changed(true);
}
void Theme::reset_state() {
@@ -1563,11 +1622,17 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
+ ClassDB::bind_method(D_METHOD("set_default_base_scale", "font_size"), &Theme::set_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_theme_base_scale);
+
ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
+ ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_theme_font);
ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
+ ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_theme_font_size);
ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item);
ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item);
@@ -1588,6 +1653,7 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 15f21b91b8..d170d53ae3 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -61,7 +61,7 @@ public:
private:
bool no_change_propagation = false;
- void _emit_theme_changed();
+ void _emit_theme_changed(bool p_notify_list_changed = false);
HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map;
HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map;
@@ -96,13 +96,19 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- static Ref<Theme> project_default_theme;
+ // Universal Theme resources used when no other theme has the item.
static Ref<Theme> default_theme;
+ static Ref<Theme> project_default_theme;
+
+ // Universal default values, final fallback for every theme.
+ static float default_base_scale;
static Ref<Texture2D> default_icon;
static Ref<StyleBox> default_style;
static Ref<Font> default_font;
static int default_font_size;
+ // Default values configurable for each individual theme.
+ float default_theme_base_scale = 0.0;
Ref<Font> default_theme_font;
int default_theme_font_size = -1;
@@ -120,16 +126,23 @@ public:
static Ref<Theme> get_project_default();
static void set_project_default(const Ref<Theme> &p_project_default);
+ static void set_default_base_scale(float p_base_scale);
static void set_default_icon(const Ref<Texture2D> &p_icon);
static void set_default_style(const Ref<StyleBox> &p_style);
static void set_default_font(const Ref<Font> &p_font);
static void set_default_font_size(int p_font_size);
+ void set_default_theme_base_scale(float p_base_scale);
+ float get_default_theme_base_scale() const;
+ bool has_default_theme_base_scale() const;
+
void set_default_theme_font(const Ref<Font> &p_default_font);
Ref<Font> get_default_theme_font() const;
+ bool has_default_theme_font() const;
void set_default_theme_font_size(int p_font_size);
int get_default_theme_font_size() const;
+ bool has_default_theme_font_size() const;
void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index fcd31143a8..a45b6b8eb6 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -31,6 +31,7 @@
#include "tile_set.h"
#include "core/core_string_names.h"
+#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/templates/local_vector.h"
@@ -39,6 +40,189 @@
#include "scene/resources/convex_polygon_shape_2d.h"
#include "servers/navigation_server_2d.h"
+/////////////////////////////// TileMapPattern //////////////////////////////////////
+
+void TileMapPattern::_set_tile_data(const Vector<int> &p_data) {
+ int c = p_data.size();
+ const int *r = p_data.ptr();
+
+ int offset = 3;
+ ERR_FAIL_COND_MSG(c % offset != 0, "Corrupted tile data.");
+
+ clear();
+
+ for (int i = 0; i < c; i += offset) {
+ const uint8_t *ptr = (const uint8_t *)&r[i];
+ uint8_t local[12];
+ for (int j = 0; j < 12; j++) {
+ local[j] = ptr[j];
+ }
+
+#ifdef BIG_ENDIAN_ENABLED
+ SWAP(local[0], local[3]);
+ SWAP(local[1], local[2]);
+ SWAP(local[4], local[7]);
+ SWAP(local[5], local[6]);
+ SWAP(local[8], local[11]);
+ SWAP(local[9], local[10]);
+#endif
+
+ int16_t x = decode_uint16(&local[0]);
+ int16_t y = decode_uint16(&local[2]);
+ uint16_t source_id = decode_uint16(&local[4]);
+ uint16_t atlas_coords_x = decode_uint16(&local[6]);
+ uint16_t atlas_coords_y = decode_uint16(&local[8]);
+ uint16_t alternative_tile = decode_uint16(&local[10]);
+ set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile);
+ }
+ emit_signal(SNAME("changed"));
+}
+
+Vector<int> TileMapPattern::_get_tile_data() const {
+ // Export tile data to raw format
+ Vector<int> data;
+ data.resize(pattern.size() * 3);
+ int *w = data.ptrw();
+
+ // Save in highest format
+
+ int idx = 0;
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ uint8_t *ptr = (uint8_t *)&w[idx];
+ encode_uint16((int16_t)(E.key.x), &ptr[0]);
+ encode_uint16((int16_t)(E.key.y), &ptr[2]);
+ encode_uint16(E.value.source_id, &ptr[4]);
+ encode_uint16(E.value.coord_x, &ptr[6]);
+ encode_uint16(E.value.coord_y, &ptr[8]);
+ encode_uint16(E.value.alternative_tile, &ptr[10]);
+ idx += 3;
+ }
+
+ return data;
+}
+
+void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords));
+
+ size = size.max(p_coords + Vector2i(1, 1));
+ pattern[p_coords] = TileMapCell(p_source_id, p_atlas_coords, p_alternative_tile);
+ emit_changed();
+}
+
+bool TileMapPattern::has_cell(const Vector2i &p_coords) const {
+ return pattern.has(p_coords);
+}
+
+void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) {
+ ERR_FAIL_COND(!pattern.has(p_coords));
+
+ pattern.erase(p_coords);
+ if (p_update_size) {
+ size = Vector2i();
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ size = size.max(E.key + Vector2i(1, 1));
+ }
+ }
+ emit_changed();
+}
+
+int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const {
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSet::INVALID_SOURCE);
+
+ return pattern[p_coords].source_id;
+}
+
+Vector2i TileMapPattern::get_cell_atlas_coords(const Vector2i &p_coords) const {
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_ATLAS_COORDS);
+
+ return pattern[p_coords].get_atlas_coords();
+}
+
+int TileMapPattern::get_cell_alternative_tile(const Vector2i &p_coords) const {
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetSource::INVALID_TILE_ALTERNATIVE);
+
+ return pattern[p_coords].alternative_tile;
+}
+
+TypedArray<Vector2i> TileMapPattern::get_used_cells() const {
+ // Returns the cells used in the tilemap.
+ TypedArray<Vector2i> a;
+ a.resize(pattern.size());
+ int i = 0;
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ Vector2i p(E.key.x, E.key.y);
+ a[i++] = p;
+ }
+
+ return a;
+}
+
+Vector2i TileMapPattern::get_size() const {
+ return size;
+}
+
+void TileMapPattern::set_size(const Vector2i &p_size) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ Vector2i coords = E.key;
+ if (p_size.x <= coords.x || p_size.y <= coords.y) {
+ ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords));
+ };
+ }
+
+ size = p_size;
+ emit_changed();
+}
+
+bool TileMapPattern::is_empty() const {
+ return pattern.is_empty();
+};
+
+void TileMapPattern::clear() {
+ size = Vector2i();
+ pattern.clear();
+ emit_changed();
+};
+
+bool TileMapPattern::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "tile_data") {
+ if (p_value.is_array()) {
+ _set_tile_data(p_value);
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
+bool TileMapPattern::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "tile_data") {
+ r_ret = _get_tile_data();
+ return true;
+ }
+ return false;
+}
+
+void TileMapPattern::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+}
+
+void TileMapPattern::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_set_tile_data", "data"), &TileMapPattern::_set_tile_data);
+ ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMapPattern::_get_tile_data);
+
+ ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell);
+ ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell);
+ ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id);
+ ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords);
+ ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile);
+
+ ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapPattern::get_used_cells);
+ ClassDB::bind_method(D_METHOD("get_size"), &TileMapPattern::get_size);
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &TileMapPattern::set_size);
+ ClassDB::bind_method(D_METHOD("is_empty"), &TileMapPattern::is_empty);
+}
+
/////////////////////////////// TileSet //////////////////////////////////////
const int TileSet::INVALID_SOURCE = -1;
@@ -66,12 +250,13 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
void TileSet::set_tile_shape(TileSet::TileShape p_shape) {
tile_shape = p_shape;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
terrain_bits_meshes_dirty = true;
tile_meshes_dirty = true;
+ notify_property_list_changed();
emit_changed();
}
TileSet::TileShape TileSet::get_tile_shape() const {
@@ -89,8 +274,8 @@ TileSet::TileLayout TileSet::get_tile_layout() const {
void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) {
tile_offset_axis = p_alignment;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
terrain_bits_meshes_dirty = true;
@@ -170,6 +355,8 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
source_ids.append(p_new_source_id);
source_ids.sort();
+ _compute_next_source_id();
+
emit_changed();
}
@@ -205,25 +392,46 @@ bool TileSet::is_uv_clipping() const {
return uv_clipping;
};
-void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) {
- ERR_FAIL_COND(p_occlusion_layers_count < 0);
- if (occlusion_layers.size() == p_occlusion_layers_count) {
- return;
- }
+int TileSet::get_occlusion_layers_count() const {
+ return occlusion_layers.size();
+};
- occlusion_layers.resize(p_occlusion_layers_count);
+void TileSet::add_occlusion_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = occlusion_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, occlusion_layers.size() + 1);
+ occlusion_layers.insert(p_index, OcclusionLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_occlusion_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_occlusion_layers_count() const {
- return occlusion_layers.size();
-};
+void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, occlusion_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, occlusion_layers.size() + 1);
+ occlusion_layers.insert(p_to_pos, occlusion_layers[p_from_index]);
+ occlusion_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_occlusion_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_occlusion_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, occlusion_layers.size());
+ occlusion_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_occlusion_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) {
ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
@@ -236,7 +444,7 @@ int TileSet::get_occlusion_layer_light_mask(int p_layer_index) const {
return occlusion_layers[p_layer_index].light_mask;
}
-void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision) {
+void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision) {
ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
occlusion_layers.write[p_layer_index].sdf_collision = p_sdf_collision;
emit_changed();
@@ -247,25 +455,45 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const {
return occlusion_layers[p_layer_index].sdf_collision;
}
-// Physics
-void TileSet::set_physics_layers_count(int p_physics_layers_count) {
- ERR_FAIL_COND(p_physics_layers_count < 0);
- if (physics_layers.size() == p_physics_layers_count) {
- return;
- }
+int TileSet::get_physics_layers_count() const {
+ return physics_layers.size();
+}
- physics_layers.resize(p_physics_layers_count);
+void TileSet::add_physics_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = physics_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, physics_layers.size() + 1);
+ physics_layers.insert(p_index, PhysicsLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_physics_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_physics_layers_count() const {
- return physics_layers.size();
+void TileSet::move_physics_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, physics_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, physics_layers.size() + 1);
+ physics_layers.insert(p_to_pos, physics_layers[p_from_index]);
+ physics_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_physics_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_physics_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, physics_layers.size());
+ physics_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_physics_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) {
@@ -301,24 +529,52 @@ Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_ind
}
// Terrains
-void TileSet::set_terrain_sets_count(int p_terrains_sets_count) {
- ERR_FAIL_COND(p_terrains_sets_count < 0);
+int TileSet::get_terrain_sets_count() const {
+ return terrain_sets.size();
+}
- terrain_sets.resize(p_terrains_sets_count);
+void TileSet::add_terrain_set(int p_index) {
+ if (p_index < 0) {
+ p_index = terrain_sets.size();
+ }
+ ERR_FAIL_INDEX(p_index, terrain_sets.size() + 1);
+ terrain_sets.insert(p_index, TerrainSet());
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_terrain_set(p_index);
+ }
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_terrain_sets_count() const {
- return terrain_sets.size();
+void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, terrain_sets.size());
+ ERR_FAIL_INDEX(p_to_pos, terrain_sets.size() + 1);
+ terrain_sets.insert(p_to_pos, terrain_sets[p_from_index]);
+ terrain_sets.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_terrain_set(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_terrain_set(int p_index) {
+ ERR_FAIL_INDEX(p_index, terrain_sets.size());
+ terrain_sets.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_terrain_set(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) {
ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
terrain_sets.write[p_terrain_set].mode = p_terrain_mode;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
notify_property_list_changed();
@@ -330,36 +586,61 @@ TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const {
return terrain_sets[p_terrain_set].mode;
}
-void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) {
+int TileSet::get_terrains_count(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
+ return terrain_sets[p_terrain_set].terrains.size();
+}
+
+void TileSet::add_terrain(int p_terrain_set, int p_index) {
ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
- ERR_FAIL_COND(p_terrains_layers_count < 0);
- if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) {
- return;
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+ if (p_index < 0) {
+ p_index = terrains.size();
}
-
- int old_size = terrain_sets[p_terrain_set].terrains.size();
- terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count);
+ ERR_FAIL_INDEX(p_index, terrains.size() + 1);
+ terrains.insert(p_index, Terrain());
// Default name and color
- for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) {
- float hue_rotate = (i * 2 % 16) / 16.0;
- Color c;
- c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
- terrain_sets.write[p_terrain_set].terrains.write[i].color = c;
- terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i));
+ float hue_rotate = (terrains.size() % 16) / 16.0;
+ Color c;
+ c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
+ terrains.write[p_index].color = c;
+ terrains.write[p_index].name = String(vformat("Terrain %d", p_index));
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_terrain(p_terrain_set, p_index);
}
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
- }
+ notify_property_list_changed();
+ emit_changed();
+}
+void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+
+ ERR_FAIL_INDEX(p_from_index, terrains.size());
+ ERR_FAIL_INDEX(p_to_pos, terrains.size() + 1);
+ terrains.insert(p_to_pos, terrains[p_from_index]);
+ terrains.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
+ }
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_terrains_count(int p_terrain_set) const {
- ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
- return terrain_sets[p_terrain_set].terrains.size();
+void TileSet::remove_terrain(int p_terrain_set, int p_index) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains;
+
+ ERR_FAIL_INDEX(p_index, terrains.size());
+ terrains.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_terrain(p_terrain_set, p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) {
@@ -485,24 +766,45 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh
}
// Navigation
-void TileSet::set_navigation_layers_count(int p_navigation_layers_count) {
- ERR_FAIL_COND(p_navigation_layers_count < 0);
- if (navigation_layers.size() == p_navigation_layers_count) {
- return;
- }
+int TileSet::get_navigation_layers_count() const {
+ return navigation_layers.size();
+}
- navigation_layers.resize(p_navigation_layers_count);
+void TileSet::add_navigation_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = navigation_layers.size();
+ }
+ ERR_FAIL_INDEX(p_index, navigation_layers.size() + 1);
+ navigation_layers.insert(p_index, NavigationLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_navigation_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_navigation_layers_count() const {
- return navigation_layers.size();
+void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, navigation_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, navigation_layers.size() + 1);
+ navigation_layers.insert(p_to_pos, navigation_layers[p_from_index]);
+ navigation_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_navigation_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_navigation_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, navigation_layers.size());
+ navigation_layers.remove(p_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_navigation_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) {
@@ -517,30 +819,52 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const {
}
// Custom data.
-void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) {
- ERR_FAIL_COND(p_custom_data_layers_count < 0);
- if (custom_data_layers.size() == p_custom_data_layers_count) {
- return;
- }
-
- custom_data_layers.resize(p_custom_data_layers_count);
+int TileSet::get_custom_data_layers_count() const {
+ return custom_data_layers.size();
+}
- for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) {
- if (E->get() >= custom_data_layers.size()) {
- custom_data_layers_by_name.erase(E);
- }
+void TileSet::add_custom_data_layer(int p_index) {
+ if (p_index < 0) {
+ p_index = custom_data_layers.size();
}
+ ERR_FAIL_INDEX(p_index, custom_data_layers.size() + 1);
+ custom_data_layers.insert(p_index, CustomDataLayer());
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->add_custom_data_layer(p_index);
}
notify_property_list_changed();
emit_changed();
}
-int TileSet::get_custom_data_layers_count() const {
- return custom_data_layers.size();
+void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, custom_data_layers.size());
+ ERR_FAIL_INDEX(p_to_pos, custom_data_layers.size() + 1);
+ custom_data_layers.insert(p_to_pos, custom_data_layers[p_from_index]);
+ custom_data_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->move_custom_data_layer(p_from_index, p_to_pos);
+ }
+ notify_property_list_changed();
+ emit_changed();
+}
+
+void TileSet::remove_custom_data_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, custom_data_layers.size());
+ custom_data_layers.remove(p_index);
+ for (KeyValue<String, int> E : custom_data_layers_by_name) {
+ if (E.value == p_index) {
+ custom_data_layers_by_name.erase(E.key);
+ break;
+ }
+ }
+
+ for (KeyValue<int, Ref<TileSetSource>> source : sources) {
+ source.value->remove_custom_data_layer(p_index);
+ }
+ notify_property_list_changed();
+ emit_changed();
}
int TileSet::get_custom_data_layer_by_name(String p_value) const {
@@ -582,8 +906,8 @@ void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) {
ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size());
custom_data_layers.write[p_layer_id].type = p_value;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
emit_changed();
@@ -721,10 +1045,10 @@ void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_
Array TileSet::get_source_level_tile_proxies() const {
Array output;
- for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
+ for (const KeyValue<int, int> &E : source_level_proxies) {
Array proxy;
- proxy.push_back(E->key());
- proxy.push_back(E->get());
+ proxy.push_back(E.key);
+ proxy.push_back(E.value);
output.push_back(proxy);
}
return output;
@@ -732,10 +1056,10 @@ Array TileSet::get_source_level_tile_proxies() const {
Array TileSet::get_coords_level_tile_proxies() const {
Array output;
- for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
+ for (const KeyValue<Array, Array> &E : coords_level_proxies) {
Array proxy;
- proxy.append_array(E->key());
- proxy.append_array(E->get());
+ proxy.append_array(E.key);
+ proxy.append_array(E.value);
output.push_back(proxy);
}
return output;
@@ -743,10 +1067,10 @@ Array TileSet::get_coords_level_tile_proxies() const {
Array TileSet::get_alternative_level_tile_proxies() const {
Array output;
- for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
+ for (const KeyValue<Array, Array> &E : alternative_level_proxies) {
Array proxy;
- proxy.append_array(E->key());
- proxy.append_array(E->get());
+ proxy.append_array(E.key);
+ proxy.append_array(E.value);
output.push_back(proxy);
}
return output;
@@ -798,9 +1122,9 @@ Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_a
void TileSet::cleanup_invalid_tile_proxies() {
// Source level.
Vector<int> source_to_remove;
- for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
- if (has_source(E->key())) {
- source_to_remove.append(E->key());
+ for (const KeyValue<int, int> &E : source_level_proxies) {
+ if (has_source(E.key)) {
+ source_to_remove.append(E.key);
}
}
for (int i = 0; i < source_to_remove.size(); i++) {
@@ -809,8 +1133,8 @@ void TileSet::cleanup_invalid_tile_proxies() {
// Coords level.
Vector<Array> coords_to_remove;
- for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
- Array a = E->key();
+ for (const KeyValue<Array, Array> &E : coords_level_proxies) {
+ Array a = E.key;
if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) {
coords_to_remove.append(a);
}
@@ -822,8 +1146,8 @@ void TileSet::cleanup_invalid_tile_proxies() {
// Alternative level.
Vector<Array> alternative_to_remove;
- for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
- Array a = E->key();
+ for (const KeyValue<Array, Array> &E : alternative_level_proxies) {
+ Array a = E.key;
if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) {
alternative_to_remove.append(a);
}
@@ -842,13 +1166,43 @@ void TileSet::clear_tile_proxies() {
emit_changed();
}
+int TileSet::add_pattern(Ref<TileMapPattern> p_pattern, int p_index) {
+ ERR_FAIL_COND_V(!p_pattern.is_valid(), -1);
+ ERR_FAIL_COND_V_MSG(p_pattern->is_empty(), -1, "Cannot add an empty pattern to the TileSet.");
+ for (unsigned int i = 0; i < patterns.size(); i++) {
+ ERR_FAIL_COND_V_MSG(patterns[i] == p_pattern, -1, "TileSet has already this pattern.");
+ }
+ ERR_FAIL_COND_V(p_index > (int)patterns.size(), -1);
+ if (p_index < 0) {
+ p_index = patterns.size();
+ }
+ patterns.insert(p_index, p_pattern);
+ emit_changed();
+ return p_index;
+}
+
+Ref<TileMapPattern> TileSet::get_pattern(int p_index) {
+ ERR_FAIL_INDEX_V(p_index, (int)patterns.size(), Ref<TileMapPattern>());
+ return patterns[p_index];
+}
+
+void TileSet::remove_pattern(int p_index) {
+ ERR_FAIL_INDEX(p_index, (int)patterns.size());
+ patterns.remove(p_index);
+ emit_changed();
+}
+
+int TileSet::get_patterns_count() {
+ return patterns.size();
+}
+
Vector<Vector2> TileSet::get_tile_shape_polygon() {
Vector<Vector2> points;
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
- points.append(Vector2(0.0, 0.0));
- points.append(Vector2(1.0, 0.0));
- points.append(Vector2(1.0, 1.0));
- points.append(Vector2(0.0, 1.0));
+ points.append(Vector2(-0.5, -0.5));
+ points.append(Vector2(0.5, -0.5));
+ points.append(Vector2(0.5, 0.5));
+ points.append(Vector2(-0.5, 0.5));
} else {
float overlap = 0.0;
switch (tile_shape) {
@@ -865,44 +1219,42 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() {
break;
}
- points.append(Vector2(0.5, 0.0));
- points.append(Vector2(0.0, overlap));
- points.append(Vector2(0.0, 1.0 - overlap));
- points.append(Vector2(0.5, 1.0));
- points.append(Vector2(1.0, 1.0 - overlap));
- points.append(Vector2(1.0, overlap));
- points.append(Vector2(0.5, 0.0));
+ points.append(Vector2(0.0, -0.5));
+ points.append(Vector2(-0.5, overlap - 0.5));
+ points.append(Vector2(-0.5, 0.5 - overlap));
+ points.append(Vector2(0.0, 0.5));
+ points.append(Vector2(0.5, 0.5 - overlap));
+ points.append(Vector2(0.5, overlap - 0.5));
if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
for (int i = 0; i < points.size(); i++) {
points.write[i] = Vector2(points[i].y, points[i].x);
}
}
}
- for (int i = 0; i < points.size(); i++) {
- points.write[i] = points[i] * tile_size - tile_size / 2;
- }
return points;
}
-void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) {
+void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) {
if (tile_meshes_dirty) {
- Vector<Vector2> uvs = get_tile_shape_polygon();
- for (int i = 0; i < uvs.size(); i++) {
- uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size;
+ Vector<Vector2> shape = get_tile_shape_polygon();
+ Vector<Vector2> uvs;
+ uvs.resize(shape.size());
+ for (int i = 0; i < shape.size(); i++) {
+ uvs.write[i] = shape[i] + Vector2(0.5, 0.5);
}
Vector<Color> colors;
- colors.resize(uvs.size());
+ colors.resize(shape.size());
colors.fill(Color(1.0, 1.0, 1.0, 1.0));
// Filled mesh.
tile_filled_mesh->clear_surfaces();
Array a;
a.resize(Mesh::ARRAY_MAX);
- a[Mesh::ARRAY_VERTEX] = uvs;
+ a[Mesh::ARRAY_VERTEX] = shape;
a[Mesh::ARRAY_TEX_UV] = uvs;
a[Mesh::ARRAY_COLOR] = colors;
- a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs);
+ a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape);
tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
// Lines mesh.
@@ -910,22 +1262,19 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p
a.clear();
a.resize(Mesh::ARRAY_MAX);
// Add the first point again when drawing lines.
- uvs.push_back(uvs[0]);
+ shape.push_back(shape[0]);
colors.push_back(colors[0]);
- a[Mesh::ARRAY_VERTEX] = uvs;
+ a[Mesh::ARRAY_VERTEX] = shape;
a[Mesh::ARRAY_COLOR] = colors;
tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
tile_meshes_dirty = false;
}
- Transform2D xform;
- xform.scale(p_region.size);
- xform.set_origin(p_region.get_position());
if (p_filled) {
- p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color);
+ p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, p_transform, p_color);
} else {
- p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color);
+ p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), p_transform, p_color);
}
}
@@ -1110,7 +1459,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) {
int terrain = tile_data->get_peering_bit_terrain(cell_neighbor);
if (terrain >= 0) {
- bit_counts[terrain] += 1;
+ if (terrain >= (int)bit_counts.size()) {
+ WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain));
+ } else {
+ bit_counts[terrain] += 1;
+ }
}
}
}
@@ -1775,8 +2128,8 @@ const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1;
#ifndef DISABLE_DEPRECATED
void TileSet::_compatibility_conversion() {
- for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
- CompatibilityTileData *ctd = E->value();
+ for (KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ CompatibilityTileData *ctd = E.value;
// Add the texture
TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource);
@@ -1814,30 +2167,30 @@ void TileSet::_compatibility_conversion() {
value_array.push_back(coords);
value_array.push_back(alternative_tile);
- if (!compatibility_tilemap_mapping.has(E->key())) {
- compatibility_tilemap_mapping[E->key()] = Map<Array, Array>();
+ if (!compatibility_tilemap_mapping.has(E.key)) {
+ compatibility_tilemap_mapping[E.key] = Map<Array, Array>();
}
- compatibility_tilemap_mapping[E->key()][key_array] = value_array;
- compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
+ compatibility_tilemap_mapping[E.key][key_array] = value_array;
+ compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v);
tile_data->set_transpose(transpose);
- tile_data->tile_set_material(ctd->material);
+ tile_data->set_material(ctd->material);
tile_data->set_modulate(ctd->modulate);
tile_data->set_z_index(ctd->z_index);
if (ctd->occluder.is_valid()) {
if (get_occlusion_layers_count() < 1) {
- set_occlusion_layers_count(1);
+ add_occlusion_layer();
}
tile_data->set_occluder(0, ctd->occluder);
}
if (ctd->navigation.is_valid()) {
if (get_navigation_layers_count() < 1) {
- set_navigation_layers_count(1);
+ add_navigation_layer();
}
tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
}
@@ -1847,7 +2200,7 @@ void TileSet::_compatibility_conversion() {
// Add the shapes.
if (ctd->shapes.size() > 0) {
if (get_physics_layers_count() < 1) {
- set_physics_layers_count(1);
+ add_physics_layer();
}
}
for (int k = 0; k < ctd->shapes.size(); k++) {
@@ -1906,29 +2259,29 @@ void TileSet::_compatibility_conversion() {
value_array.push_back(coords);
value_array.push_back(alternative_tile);
- if (!compatibility_tilemap_mapping.has(E->key())) {
- compatibility_tilemap_mapping[E->key()] = Map<Array, Array>();
+ if (!compatibility_tilemap_mapping.has(E.key)) {
+ compatibility_tilemap_mapping[E.key] = Map<Array, Array>();
}
- compatibility_tilemap_mapping[E->key()][key_array] = value_array;
- compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
+ compatibility_tilemap_mapping[E.key][key_array] = value_array;
+ compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v);
tile_data->set_transpose(transpose);
- tile_data->tile_set_material(ctd->material);
+ tile_data->set_material(ctd->material);
tile_data->set_modulate(ctd->modulate);
tile_data->set_z_index(ctd->z_index);
if (ctd->autotile_occluder_map.has(coords)) {
if (get_occlusion_layers_count() < 1) {
- set_occlusion_layers_count(1);
+ add_occlusion_layer();
}
tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]);
}
if (ctd->autotile_navpoly_map.has(coords)) {
if (get_navigation_layers_count() < 1) {
- set_navigation_layers_count(1);
+ add_navigation_layer();
}
tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
}
@@ -1942,7 +2295,7 @@ void TileSet::_compatibility_conversion() {
// Add the shapes.
if (ctd->shapes.size() > 0) {
if (get_physics_layers_count() < 1) {
- set_physics_layers_count(1);
+ add_physics_layer();
}
}
for (int k = 0; k < ctd->shapes.size(); k++) {
@@ -1992,8 +2345,8 @@ void TileSet::_compatibility_conversion() {
}
// Reset compatibility data
- for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ memdelete(E.value);
}
compatibility_data = Map<int, CompatibilityTileData *>();
}
@@ -2206,15 +2559,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "light_mask") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= occlusion_layers.size()) {
- set_occlusion_layers_count(index + 1);
+ while (index >= occlusion_layers.size()) {
+ add_occlusion_layer();
}
set_occlusion_layer_light_mask(index, p_value);
return true;
} else if (components[1] == "sdf_collision") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
- if (index >= occlusion_layers.size()) {
- set_occlusion_layers_count(index + 1);
+ while (index >= occlusion_layers.size()) {
+ add_occlusion_layer();
}
set_occlusion_layer_sdf_collision(index, p_value);
return true;
@@ -2225,23 +2578,22 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "collision_layer") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_collision_layer(index, p_value);
return true;
} else if (components[1] == "collision_mask") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_collision_mask(index, p_value);
return true;
} else if (components[1] == "physics_material") {
Ref<PhysicsMaterial> physics_material = p_value;
- ERR_FAIL_COND_V(!physics_material.is_valid(), false);
- if (index >= physics_layers.size()) {
- set_physics_layers_count(index + 1);
+ while (index >= physics_layers.size()) {
+ add_physics_layer();
}
set_physics_layer_physics_material(index, physics_material);
return true;
@@ -2252,37 +2604,30 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(terrain_set_index < 0, false);
if (components[1] == "mode") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value)));
- } else if (components[1] == "terrains_count") {
- ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
- }
- set_terrains_count(terrain_set_index, p_value);
- return true;
} else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
ERR_FAIL_COND_V(terrain_index < 0, false);
if (components[2] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
- if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
- set_terrains_count(terrain_set_index, terrain_index + 1);
+ while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ add_terrain(terrain_set_index);
}
set_terrain_name(terrain_set_index, terrain_index, p_value);
return true;
} else if (components[2] == "color") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false);
- if (terrain_set_index >= terrain_sets.size()) {
- set_terrain_sets_count(terrain_set_index + 1);
+ while (terrain_set_index >= terrain_sets.size()) {
+ add_terrain_set();
}
- if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
- set_terrains_count(terrain_set_index, terrain_index + 1);
+ while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ add_terrain(terrain_set_index);
}
set_terrain_color(terrain_set_index, terrain_index, p_value);
return true;
@@ -2294,8 +2639,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "layers") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= navigation_layers.size()) {
- set_navigation_layers_count(index + 1);
+ while (index >= navigation_layers.size()) {
+ add_navigation_layer();
}
set_navigation_layer_layers(index, p_value);
return true;
@@ -2306,15 +2651,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(index < 0, false);
if (components[1] == "name") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
- if (index >= custom_data_layers.size()) {
- set_custom_data_layers_count(index + 1);
+ while (index >= custom_data_layers.size()) {
+ add_custom_data_layer();
}
set_custom_data_name(index, p_value);
return true;
} else if (components[1] == "type") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
- if (index >= custom_data_layers.size()) {
- set_custom_data_layers_count(index + 1);
+ while (index >= custom_data_layers.size()) {
+ add_custom_data_layer();
}
set_custom_data_type(index, Variant::Type(int(p_value)));
return true;
@@ -2352,6 +2697,12 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
return true;
}
return false;
+ } else if (components.size() == 1 && components[0].begins_with("pattern_") && components[0].trim_prefix("pattern_").is_valid_int()) {
+ int pattern_index = components[0].trim_prefix("pattern_").to_int();
+ for (int i = patterns.size(); i <= pattern_index; i++) {
+ add_pattern(p_value);
+ }
+ return true;
}
#ifndef DISABLE_DEPRECATED
@@ -2402,9 +2753,6 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
if (components[1] == "mode") {
r_ret = get_terrain_set_mode(terrain_set_index);
return true;
- } else if (components[1] == "terrains_count") {
- r_ret = get_terrains_count(terrain_set_index);
- return true;
} else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) {
int terrain_index = components[1].trim_prefix("terrain_").to_int();
if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
@@ -2454,30 +2802,37 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components.size() == 2 && components[0] == "tile_proxies") {
if (components[1] == "source_level") {
Array a;
- for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
- a.push_back(E->key());
- a.push_back(E->get());
+ for (const KeyValue<int, int> &E : source_level_proxies) {
+ a.push_back(E.key);
+ a.push_back(E.value);
}
r_ret = a;
return true;
} else if (components[1] == "coords_level") {
Array a;
- for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
- a.push_back(E->key());
- a.push_back(E->get());
+ for (const KeyValue<Array, Array> &E : coords_level_proxies) {
+ a.push_back(E.key);
+ a.push_back(E.value);
}
r_ret = a;
return true;
} else if (components[1] == "alternative_level") {
Array a;
- for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
- a.push_back(E->key());
- a.push_back(E->get());
+ for (const KeyValue<Array, Array> &E : alternative_level_proxies) {
+ a.push_back(E.key);
+ a.push_back(E.value);
}
r_ret = a;
return true;
}
return false;
+ } else if (components.size() == 1 && components[0].begins_with("pattern_") && components[0].trim_prefix("pattern_").is_valid_int()) {
+ int pattern_index = components[0].trim_prefix("pattern_").to_int();
+ if (pattern_index < 0 || pattern_index >= (int)patterns.size()) {
+ return false;
+ }
+ r_ret = patterns[pattern_index];
+ return true;
}
return false;
@@ -2522,7 +2877,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) {
p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides"));
- p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index)));
for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index)));
p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index)));
@@ -2548,8 +2903,8 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Sources.
// Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first.
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
// Tile Proxies.
@@ -2558,18 +2913,31 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+
+ // Patterns.
+ for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("pattern_%d", pattern_index), PROPERTY_HINT_RESOURCE_TYPE, "TileMapPattern", PROPERTY_USAGE_NOEDITOR));
+ }
+}
+
+void TileSet::_validate_property(PropertyInfo &property) const {
+ if (property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) {
+ property.usage ^= PROPERTY_USAGE_READ_ONLY;
+ } else if (property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) {
+ property.usage ^= PROPERTY_USAGE_READ_ONLY;
+ }
}
void TileSet::_bind_methods() {
// Sources management.
ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id);
- ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE));
+ ClassDB::bind_method(D_METHOD("add_source", "source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE));
ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source);
- ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id);
+ ClassDB::bind_method(D_METHOD("set_source_id", "source_id", "new_source_id"), &TileSet::set_source_id);
ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count);
ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id);
- ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source);
- ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source);
+ ClassDB::bind_method(D_METHOD("has_source", "source_id"), &TileSet::has_source);
+ ClassDB::bind_method(D_METHOD("get_source", "source_id"), &TileSet::get_source);
// Shape and layout.
ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape);
@@ -2590,16 +2958,20 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping);
ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping);
- ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count);
ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("add_occlusion_layer", "to_position"), &TileSet::add_occlusion_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_occlusion_layer", "layer_index", "to_position"), &TileSet::move_occlusion_layer);
+ ClassDB::bind_method(D_METHOD("remove_occlusion_layer", "layer_index"), &TileSet::remove_occlusion_layer);
ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask);
- ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask", "layer_index"), &TileSet::get_occlusion_layer_light_mask);
ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision);
- ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision", "layer_index"), &TileSet::get_occlusion_layer_sdf_collision);
// Physics
- ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count);
ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("add_physics_layer", "to_position"), &TileSet::add_physics_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_physics_layer", "layer_index", "to_position"), &TileSet::move_physics_layer);
+ ClassDB::bind_method(D_METHOD("remove_physics_layer", "layer_index"), &TileSet::remove_physics_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer);
ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask);
@@ -2608,27 +2980,35 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material);
// Terrains
- ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count);
ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("add_terrain_set", "to_position"), &TileSet::add_terrain_set, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_terrain_set", "terrain_set", "to_position"), &TileSet::move_terrain_set);
+ ClassDB::bind_method(D_METHOD("remove_terrain_set", "terrain_set"), &TileSet::remove_terrain_set);
ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode);
ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode);
- ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count);
ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count);
+ ClassDB::bind_method(D_METHOD("add_terrain", "terrain_set", "to_position"), &TileSet::add_terrain, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_terrain", "terrain_set", "terrain_index", "to_position"), &TileSet::move_terrain);
+ ClassDB::bind_method(D_METHOD("remove_terrain", "terrain_set", "terrain_index"), &TileSet::remove_terrain);
ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name);
ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name);
ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color);
ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color);
// Navigation
- ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count);
ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("add_navigation_layer", "to_position"), &TileSet::add_navigation_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_navigation_layer", "layer_index", "to_position"), &TileSet::move_navigation_layer);
+ ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer);
ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers);
// Custom data
- ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count);
ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count);
+ ClassDB::bind_method(D_METHOD("add_custom_data_layer", "to_position"), &TileSet::add_custom_data_layer, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("move_custom_data_layer", "layer_index", "to_position"), &TileSet::move_custom_data_layer);
+ ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer);
// Tile proxies
ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy);
@@ -2651,21 +3031,27 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("cleanup_invalid_tile_proxies"), &TileSet::cleanup_invalid_tile_proxies);
ClassDB::bind_method(D_METHOD("clear_tile_proxies"), &TileSet::clear_tile_proxies);
+ // Patterns
+ ClassDB::bind_method(D_METHOD("add_pattern", "pattern", "index"), &TileSet::add_pattern, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_pattern", "index"), &TileSet::get_pattern, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("remove_pattern", "index"), &TileSet::remove_pattern);
+ ClassDB::bind_method(D_METHOD("get_patterns_count"), &TileSet::get_patterns_count);
+
ADD_GROUP("Rendering", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count");
+ ADD_ARRAY("occlusion_layers", "occlusion_layer_");
ADD_GROUP("Physics", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count");
+ ADD_ARRAY("physics_layers", "physics_layer_");
ADD_GROUP("Terrains", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count");
+ ADD_ARRAY("terrain_sets", "terrain_set_");
ADD_GROUP("Navigation", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count");
+ ADD_ARRAY("navigation_layers", "navigation_layer_");
ADD_GROUP("Custom data", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count");
+ ADD_ARRAY("custom_data_layers", "custom_data_layer_");
// -- Enum binding --
BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE);
@@ -2713,8 +3099,8 @@ TileSet::TileSet() {
TileSet::~TileSet() {
#ifndef DISABLE_DEPRECATED
- for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ memdelete(E.value);
}
#endif // DISABLE_DEPRECATED
while (!source_ids.is_empty()) {
@@ -2728,33 +3114,189 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) {
tile_set = p_tile_set;
}
+void TileSetSource::_bind_methods() {
+ // Base tiles
+ ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetSource::get_tile_id);
+ ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetSource::has_tile);
+
+ // Alternative tiles
+ ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetSource::get_alternative_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetSource::get_alternative_tile_id);
+ ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetSource::has_alternative_tile);
+}
+
/////////////////////////////// TileSetAtlasSource //////////////////////////////////////
void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) {
tile_set = p_tile_set;
// Set the TileSet on all TileData.
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
- E_alternative->get()->set_tile_set(tile_set);
+ for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
+ for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->set_tile_set(tile_set);
}
}
}
void TileSetAtlasSource::notify_tile_data_properties_should_change() {
// Set the TileSet on all TileData.
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
- E_alternative->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
+ for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->notify_tile_data_properties_should_change();
+ }
+ }
+}
+
+void TileSetAtlasSource::add_occlusion_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_occlusion_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_occlusion_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_occlusion_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_occlusion_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_physics_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_physics_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_physics_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_physics_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_physics_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_physics_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_terrain_set(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_terrain_set(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_terrain_set(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_terrain_set(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_terrain_set(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_terrain_set(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_terrain(int p_terrain_set, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_terrain(p_terrain_set, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_terrain(int p_terrain_set, int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_terrain(p_terrain_set, p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_navigation_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_navigation_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_navigation_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_navigation_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_navigation_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_navigation_layer(p_index);
+ }
+ }
+}
+
+void TileSetAtlasSource::add_custom_data_layer(int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->add_custom_data_layer(p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->move_custom_data_layer(p_from_index, p_to_pos);
+ }
+ }
+}
+
+void TileSetAtlasSource::remove_custom_data_layer(int p_index) {
+ for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) {
+ for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->remove_custom_data_layer(p_index);
}
}
}
void TileSetAtlasSource::reset_state() {
// Reset all TileData.
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
- E_alternative->get()->reset_state();
+ for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
+ for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->reset_state();
}
}
}
@@ -2762,6 +3304,7 @@ void TileSetAtlasSource::reset_state() {
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
texture = p_texture;
+ _clear_tiles_outside_texture();
emit_changed();
}
@@ -2777,6 +3320,7 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
margins = p_margins;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_margins() const {
@@ -2791,6 +3335,7 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
separation = p_separation;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_separation() const {
@@ -2805,6 +3350,7 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
texture_region_size = p_tile_size;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_texture_region_size() const {
@@ -2850,8 +3396,32 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value)
// Properties.
if (components[1] == "size_in_atlas") {
move_tile_in_atlas(coords, coords, p_value);
+ return true;
} else if (components[1] == "next_alternative_id") {
tiles[coords].next_alternative_id = p_value;
+ return true;
+ } else if (components[1] == "animation_columns") {
+ set_tile_animation_columns(coords, p_value);
+ return true;
+ } else if (components[1] == "animation_separation") {
+ set_tile_animation_separation(coords, p_value);
+ return true;
+ } else if (components[1] == "animation_speed") {
+ set_tile_animation_speed(coords, p_value);
+ return true;
+ } else if (components[1] == "animation_frames_count") {
+ set_tile_animation_frames_count(coords, p_value);
+ return true;
+ } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) {
+ int frame = components[1].trim_prefix("animation_frame_").to_int();
+ if (components[2] == "duration") {
+ if (frame >= get_tile_animation_frames_count(coords)) {
+ set_tile_animation_frames_count(coords, frame + 1);
+ }
+ set_tile_animation_frame_duration(coords, frame, p_value);
+ return true;
+ }
+ return false;
} else if (components[1].is_valid_int()) {
int alternative_id = components[1].to_int();
if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) {
@@ -2897,6 +3467,28 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "next_alternative_id") {
r_ret = tiles[coords].next_alternative_id;
return true;
+ } else if (components[1] == "animation_columns") {
+ r_ret = get_tile_animation_columns(coords);
+ return true;
+ } else if (components[1] == "animation_separation") {
+ r_ret = get_tile_animation_separation(coords);
+ return true;
+ } else if (components[1] == "animation_speed") {
+ r_ret = get_tile_animation_speed(coords);
+ return true;
+ } else if (components[1] == "animation_frames_count") {
+ r_ret = get_tile_animation_frames_count(coords);
+ return true;
+ } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) {
+ int frame = components[1].trim_prefix("animation_frame_").to_int();
+ if (frame < 0 || frame >= get_tile_animation_frames_count(coords)) {
+ return false;
+ }
+ if (components[2] == "duration") {
+ r_ret = get_tile_animation_frame_duration(coords, frame);
+ return true;
+ }
+ return false;
} else if (components[1].is_valid_int()) {
int alternative_id = components[1].to_int();
if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) {
@@ -2921,45 +3513,78 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
// Atlases data.
PropertyInfo property_info;
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (const KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
List<PropertyInfo> tile_property_list;
// size_in_atlas
property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().size_in_atlas == Vector2i(1, 1)) {
+ if (E_tile.value.size_in_atlas == Vector2i(1, 1)) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// next_alternative_id
property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().next_alternative_id == 1) {
+ if (E_tile.value.next_alternative_id == 1) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ // animation_columns.
+ property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile.value.animation_columns == 0) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ // animation_separation.
+ property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile.value.animation_separation == Vector2i()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ // animation_speed.
+ property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile.value.animation_speed == 1.0) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ // animation_frames_count.
+ tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK));
+
+ // animation_frame_*.
+ bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2;
+ for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) {
+ property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (!store_durations) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+ }
+
+ for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
// Add a dummy property to show the alternative exists.
- tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
// Get the alternative tile's properties and append them to the list of properties.
List<PropertyInfo> alternative_property_list;
- E_alternative->get()->get_property_list(&alternative_property_list);
+ E_alternative.value->get_property_list(&alternative_property_list);
for (PropertyInfo &alternative_property_info : alternative_property_list) {
- bool valid;
- Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid);
- Variant value = E_alternative->get()->get(alternative_property_info.name);
- if (valid && value == default_value) {
- property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name);
+ Variant value = E_alternative.value->get(alternative_property_info.name);
+ if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
+ alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
- alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name);
+ alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name);
tile_property_list.push_back(alternative_property_info);
}
}
// Add all alternative.
for (PropertyInfo &tile_property_info : tile_property_list) {
- tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name);
+ tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name);
p_list->push_back(tile_property_info);
}
}
@@ -2969,33 +3594,27 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
// Create a tile if it does not exists.
ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0);
ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
- for (int x = 0; x < p_size.x; x++) {
- for (int y = 0; y < p_size.y; y++) {
- Vector2i coords = p_atlas_coords + Vector2i(x, y);
- ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords));
- }
- }
+
+ bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1);
+ ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile. The tile is outside the texture or tiles are already present in the space the tile would cover.");
+
+ // Initialize the tile data.
+ TileAlternativesData tad;
+ tad.size_in_atlas = p_size;
+ tad.animation_frames_durations.push_back(1.0);
+ tad.alternatives[0] = memnew(TileData);
+ tad.alternatives[0]->set_tile_set(tile_set);
+ tad.alternatives[0]->set_allow_transform(false);
+ tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed));
+ tad.alternatives[0]->notify_property_list_changed();
+ tad.alternatives_ids.append(0);
// Create and resize the tile.
- tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData());
+ tiles.insert(p_atlas_coords, tad);
tiles_ids.append(p_atlas_coords);
tiles_ids.sort();
- tiles[p_atlas_coords].size_in_atlas = p_size;
- tiles[p_atlas_coords].alternatives[0] = memnew(TileData);
- tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set);
- tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false);
- tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed));
- tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed();
- tiles[p_atlas_coords].alternatives_ids.append(0);
-
- // Add all covered positions to the mapping cache
- for (int x = 0; x < p_size.x; x++) {
- for (int y = 0; y < p_size.y; y++) {
- Vector2i coords = p_atlas_coords + Vector2i(x, y);
- _coords_mapping_cache[coords] = p_atlas_coords;
- }
- }
+ _create_coords_mapping_cache(p_atlas_coords);
emit_signal(SNAME("changed"));
}
@@ -3004,18 +3623,11 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
// Remove all covered positions from the mapping cache
- Size2i size = tiles[p_atlas_coords].size_in_atlas;
-
- for (int x = 0; x < size.x; x++) {
- for (int y = 0; y < size.y; y++) {
- Vector2i coords = p_atlas_coords + Vector2i(x, y);
- _coords_mapping_cache.erase(coords);
- }
- }
+ _clear_coords_mapping_cache(p_atlas_coords);
// Free tile data.
- for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
- memdelete(E_tile_data->get());
+ for (const KeyValue<int, TileData *> &E_tile_data : tiles[p_atlas_coords].alternatives) {
+ memdelete(E_tile_data.value);
}
// Delete the tile
@@ -3038,6 +3650,118 @@ Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const {
return _coords_mapping_cache[p_atlas_coords];
}
+void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_COND(p_frame_columns < 0);
+
+ TileAlternativesData &tad = tiles[p_atlas_coords];
+ bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, p_frame_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords);
+ ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
+
+ _clear_coords_mapping_cache(p_atlas_coords);
+
+ tiles[p_atlas_coords].animation_columns = p_frame_columns;
+
+ _create_coords_mapping_cache(p_atlas_coords);
+
+ emit_signal(SNAME("changed"));
+}
+
+int TileSetAtlasSource::get_tile_animation_columns(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ return tiles[p_atlas_coords].animation_columns;
+}
+
+void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_COND(p_separation.x < 0 || p_separation.y < 0);
+
+ TileAlternativesData &tad = tiles[p_atlas_coords];
+ bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, p_separation, tad.animation_frames_durations.size(), p_atlas_coords);
+ ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
+
+ _clear_coords_mapping_cache(p_atlas_coords);
+
+ tiles[p_atlas_coords].animation_separation = p_separation;
+
+ _create_coords_mapping_cache(p_atlas_coords);
+
+ emit_signal(SNAME("changed"));
+}
+
+Vector2i TileSetAtlasSource::get_tile_animation_separation(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ return tiles[p_atlas_coords].animation_separation;
+}
+
+void TileSetAtlasSource::set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_COND(p_speed <= 0);
+
+ tiles[p_atlas_coords].animation_speed = p_speed;
+
+ emit_signal(SNAME("changed"));
+}
+
+real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1.0, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ return tiles[p_atlas_coords].animation_speed;
+}
+
+void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_COND(p_frames_count < 1);
+
+ TileAlternativesData &tad = tiles[p_atlas_coords];
+ bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords);
+ ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover.");
+
+ _clear_coords_mapping_cache(p_atlas_coords);
+
+ int old_size = tiles[p_atlas_coords].animation_frames_durations.size();
+ tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count);
+ for (int i = old_size; i < p_frames_count; i++) {
+ tiles[p_atlas_coords].animation_frames_durations[i] = 1.0;
+ }
+
+ _create_coords_mapping_cache(p_atlas_coords);
+
+ notify_property_list_changed();
+
+ emit_signal(SNAME("changed"));
+}
+
+int TileSetAtlasSource::get_tile_animation_frames_count(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ return tiles[p_atlas_coords].animation_frames_durations.size();
+}
+
+void TileSetAtlasSource::set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_INDEX(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size());
+ ERR_FAIL_COND(p_duration <= 0.0);
+
+ tiles[p_atlas_coords].animation_frames_durations[p_frame_index] = p_duration;
+
+ emit_signal(SNAME("changed"));
+}
+
+real_t TileSetAtlasSource::get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_INDEX_V(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size(), 0.0);
+ return tiles[p_atlas_coords].animation_frames_durations[p_frame_index];
+}
+
+real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+
+ real_t sum = 0.0;
+ for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) {
+ sum += tiles[p_atlas_coords].animation_frames_durations[frame];
+ }
+ return sum;
+}
+
Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
@@ -3053,16 +3777,75 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const {
return tiles_ids[p_index];
}
-Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const {
+bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const {
+ if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) {
+ return false;
+ }
+ if (p_size.x <= 0 || p_size.y <= 0) {
+ return false;
+ }
+ Size2i atlas_grid_size = get_atlas_grid_size();
+ for (int frame = 0; frame < p_frames_count; frame++) {
+ Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0));
+ for (int x = 0; x < p_size.x; x++) {
+ for (int y = 0; y < p_size.y; y++) {
+ Vector2i coords = frame_coords + Vector2i(x, y);
+ if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) {
+ return false;
+ }
+ if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) {
+ ERR_FAIL_COND_V(p_margins.x < 0 || p_margins.y < 0, PackedVector2Array());
+ ERR_FAIL_COND_V(p_separation.x < 0 || p_separation.y < 0, PackedVector2Array());
+ ERR_FAIL_COND_V(p_texture_region_size.x <= 0 || p_texture_region_size.y <= 0, PackedVector2Array());
+
+ // Compute the new atlas grid size.
+ Size2 new_grid_size;
+ if (p_texture.is_valid()) {
+ Size2i valid_area = p_texture->get_size() - p_margins;
+
+ // Compute the number of valid tiles in the tiles atlas
+ if (valid_area.x >= p_texture_region_size.x && valid_area.y >= p_texture_region_size.y) {
+ valid_area -= p_texture_region_size;
+ new_grid_size = Size2i(1, 1) + valid_area / (p_texture_region_size + p_separation);
+ }
+ }
+
+ Vector<Vector2> output;
+ for (KeyValue<Vector2i, TileAlternativesData> &E : tiles) {
+ for (unsigned int frame = 0; frame < E.value.animation_frames_durations.size(); frame++) {
+ Vector2i frame_coords = E.key + (E.value.size_in_atlas + E.value.animation_separation) * ((E.value.animation_columns > 0) ? Vector2i(frame % E.value.animation_columns, frame / E.value.animation_columns) : Vector2i(frame, 0));
+ frame_coords += E.value.size_in_atlas;
+ if (frame_coords.x > new_grid_size.x || frame_coords.y > new_grid_size.y) {
+ output.push_back(E.key);
+ break;
+ }
+ }
+ }
+ return output;
+}
+
+Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i());
- Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas;
+ const TileAlternativesData &tad = tiles[p_atlas_coords];
+
+ Vector2i size_in_atlas = tad.size_in_atlas;
Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
- Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation));
+ Vector2i frame_coords = p_atlas_coords + (size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0));
+ Vector2 origin = margins + (frame_coords * (texture_region_size + separation));
return Rect2(origin, region_size);
- ;
}
Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const {
@@ -3074,63 +3857,29 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset();
if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
- effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x);
- effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y);
+ effective_texture_offset = effective_texture_offset.clamp(-margin, margin);
}
return effective_texture_offset;
}
-bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const {
- ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
-
- Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
- if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) {
- return false;
- }
-
- Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
- ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false);
-
- Size2i grid_size = get_atlas_grid_size();
- if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) {
- return false;
- }
-
- Rect2i new_rect = Rect2i(new_atlas_coords, size);
- // Check if the new tile can fit in the new rect.
- for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) {
- for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) {
- Vector2i coords = get_tile_at_coords(Vector2i(x, y));
- if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) {
- return false;
- }
- }
- }
-
- return true;
-}
-
void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
- bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size);
- ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size));
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ TileAlternativesData &tad = tiles[p_atlas_coords];
// Compute the actual new rect from arguments.
Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
- Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
+ Vector2i new_size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tad.size_in_atlas;
- if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) {
+ if (new_atlas_coords == p_atlas_coords && new_size == tad.size_in_atlas) {
return;
}
- // Remove all covered positions from the mapping cache.
- Size2i old_size = tiles[p_atlas_coords].size_in_atlas;
- for (int x = 0; x < old_size.x; x++) {
- for (int y = 0; y < old_size.y; y++) {
- Vector2i coords = p_atlas_coords + Vector2i(x, y);
- _coords_mapping_cache.erase(coords);
- }
- }
+ bool room_for_tile = has_room_for_tile(new_atlas_coords, new_size, tad.animation_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords);
+ ERR_FAIL_COND_MSG(!room_for_tile, vformat("Cannot move tile at position %s with size %s. Tile already present.", new_atlas_coords, new_size));
+
+ _clear_coords_mapping_cache(p_atlas_coords);
// Move the tile and update its size.
if (new_atlas_coords != p_atlas_coords) {
@@ -3141,47 +3890,13 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
tiles_ids.append(new_atlas_coords);
tiles_ids.sort();
}
- tiles[new_atlas_coords].size_in_atlas = size;
+ tiles[new_atlas_coords].size_in_atlas = new_size;
- // Add all covered positions to the mapping cache again.
- for (int x = 0; x < size.x; x++) {
- for (int y = 0; y < size.y; y++) {
- Vector2i coords = new_atlas_coords + Vector2i(x, y);
- _coords_mapping_cache[coords] = new_atlas_coords;
- }
- }
+ _create_coords_mapping_cache(new_atlas_coords);
emit_signal(SNAME("changed"));
}
-bool TileSetAtlasSource::has_tiles_outside_texture() {
- Vector2i grid_size = get_atlas_grid_size();
- Vector<Vector2i> to_remove;
-
- for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
- if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
- return true;
- }
- }
-
- return false;
-}
-
-void TileSetAtlasSource::clear_tiles_outside_texture() {
- Vector2i grid_size = get_atlas_grid_size();
- Vector<Vector2i> to_remove;
-
- for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
- if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
- to_remove.append(E->key());
- }
- }
-
- for (int i = 0; i < to_remove.size(); i++) {
- remove_tile(to_remove[i]);
- }
-}
-
int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
@@ -3273,45 +3988,48 @@ void TileSetAtlasSource::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size");
// Base tiles
ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
- ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile);
- ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
- ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count);
- ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id);
-
+ ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS));
+ ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change);
ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
+ ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns);
+ ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation);
+ ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed);
+ ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count);
+ ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_frame_duration", "atlas_coords", "frame_index"), &TileSetAtlasSource::get_tile_animation_frame_duration);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_total_duration", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_total_duration);
+
// Alternative tiles
ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile);
ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id);
- ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile);
ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id);
- ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count);
- ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id);
-
- ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data);
+ ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::get_tile_data);
// Helpers.
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
- ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
- ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
- ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region);
+ ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
}
TileSetAtlasSource::~TileSetAtlasSource() {
// Free everything needed.
- for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) {
- for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
- memdelete(E_tile_data->get());
+ for (KeyValue<Vector2i, TileAlternativesData> &E_alternatives : tiles) {
+ for (KeyValue<int, TileData *> &E_tile_data : E_alternatives.value.alternatives) {
+ memdelete(E_tile_data.value);
}
}
}
@@ -3338,6 +4056,59 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo
};
}
+void TileSetAtlasSource::_clear_coords_mapping_cache(Vector2i p_atlas_coords) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ TileAlternativesData &tad = tiles[p_atlas_coords];
+ for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) {
+ Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0));
+ for (int x = 0; x < tad.size_in_atlas.x; x++) {
+ for (int y = 0; y < tad.size_in_atlas.y; y++) {
+ Vector2i coords = frame_coords + Vector2i(x, y);
+ if (!_coords_mapping_cache.has(coords)) {
+ WARN_PRINT(vformat("TileSetAtlasSource has no cached tile at position %s, the position cache might be corrupted.", coords));
+ } else {
+ if (_coords_mapping_cache[coords] != p_atlas_coords) {
+ WARN_PRINT(vformat("The position cache at position %s is pointing to a wrong tile, the position cache might be corrupted.", coords));
+ }
+ _coords_mapping_cache.erase(coords);
+ }
+ }
+ }
+ }
+}
+
+void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+
+ TileAlternativesData &tad = tiles[p_atlas_coords];
+ for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) {
+ Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0));
+ for (int x = 0; x < tad.size_in_atlas.x; x++) {
+ for (int y = 0; y < tad.size_in_atlas.y; y++) {
+ Vector2i coords = frame_coords + Vector2i(x, y);
+ if (_coords_mapping_cache.has(coords)) {
+ WARN_PRINT(vformat("The cache already has a tile for position %s, the position cache might be corrupted.", coords));
+ }
+ _coords_mapping_cache[coords] = p_atlas_coords;
+ }
+ }
+ }
+}
+
+void TileSetAtlasSource::_clear_tiles_outside_texture() {
+ LocalVector<Vector2i> to_remove;
+
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
+ to_remove.push_back(E.key);
+ }
+ }
+
+ for (unsigned int i = 0; i < to_remove.size(); i++) {
+ remove_tile(to_remove[i]);
+ }
+}
+
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {
@@ -3511,16 +4282,6 @@ void TileSetScenesCollectionSource::_get_property_list(List<PropertyInfo> *p_lis
}
void TileSetScenesCollectionSource::_bind_methods() {
- // Base tiles
- ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count);
- ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id);
- ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile);
-
- // Alternative tiles
- ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count);
- ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id);
- ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile);
-
ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count);
ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id);
ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id);
@@ -3575,6 +4336,155 @@ void TileData::notify_tile_data_properties_should_change() {
emit_signal(SNAME("changed"));
}
+void TileData::add_occlusion_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = occluders.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
+ occluders.insert(p_to_pos, Ref<OccluderPolygon2D>());
+}
+
+void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, occluders.size());
+ ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
+ occluders.insert(p_to_pos, occluders[p_from_index]);
+ occluders.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_occlusion_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, occluders.size());
+ occluders.remove(p_index);
+}
+
+void TileData::add_physics_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = physics.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
+ physics.insert(p_to_pos, PhysicsLayerTileData());
+}
+
+void TileData::move_physics_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, physics.size());
+ ERR_FAIL_INDEX(p_to_pos, physics.size() + 1);
+ physics.insert(p_to_pos, physics[p_from_index]);
+ physics.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_physics_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, physics.size());
+ physics.remove(p_index);
+}
+
+void TileData::add_terrain_set(int p_to_pos) {
+ if (p_to_pos >= 0 && p_to_pos <= terrain_set) {
+ terrain_set += 1;
+ }
+}
+
+void TileData::move_terrain_set(int p_from_index, int p_to_pos) {
+ if (p_from_index == terrain_set) {
+ terrain_set = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos;
+ } else {
+ if (p_from_index < terrain_set) {
+ terrain_set -= 1;
+ }
+ if (p_to_pos <= terrain_set) {
+ terrain_set += 1;
+ }
+ }
+}
+
+void TileData::remove_terrain_set(int p_index) {
+ if (p_index == terrain_set) {
+ terrain_set = -1;
+ for (int i = 0; i < 16; i++) {
+ terrain_peering_bits[i] = -1;
+ }
+ } else if (terrain_set > p_index) {
+ terrain_set -= 1;
+ }
+}
+
+void TileData::add_terrain(int p_terrain_set, int p_to_pos) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (p_to_pos >= 0 && p_to_pos <= terrain_peering_bits[i]) {
+ terrain_peering_bits[i] += 1;
+ }
+ }
+ }
+}
+
+void TileData::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (p_from_index == terrain_peering_bits[i]) {
+ terrain_peering_bits[i] = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos;
+ } else {
+ if (p_from_index < terrain_peering_bits[i]) {
+ terrain_peering_bits[i] -= 1;
+ }
+ if (p_to_pos <= terrain_peering_bits[i]) {
+ terrain_peering_bits[i] += 1;
+ }
+ }
+ }
+ }
+}
+
+void TileData::remove_terrain(int p_terrain_set, int p_index) {
+ if (terrain_set == p_terrain_set) {
+ for (int i = 0; i < 16; i++) {
+ if (terrain_peering_bits[i] == p_index) {
+ terrain_peering_bits[i] = -1;
+ } else if (terrain_peering_bits[i] > p_index) {
+ terrain_peering_bits[i] -= 1;
+ }
+ }
+ }
+}
+
+void TileData::add_navigation_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = navigation.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
+ navigation.insert(p_to_pos, Ref<NavigationPolygon>());
+}
+
+void TileData::move_navigation_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, navigation.size());
+ ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
+ navigation.insert(p_to_pos, navigation[p_from_index]);
+ navigation.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_navigation_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, navigation.size());
+ navigation.remove(p_index);
+}
+
+void TileData::add_custom_data_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = custom_data.size();
+ }
+ ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
+ custom_data.insert(p_to_pos, Variant());
+}
+
+void TileData::move_custom_data_layer(int p_from_index, int p_to_pos) {
+ ERR_FAIL_INDEX(p_from_index, custom_data.size());
+ ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1);
+ custom_data.insert(p_to_pos, navigation[p_from_index]);
+ custom_data.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index);
+}
+
+void TileData::remove_custom_data_layer(int p_index) {
+ ERR_FAIL_INDEX(p_index, custom_data.size());
+ custom_data.remove(p_index);
+}
+
void TileData::reset_state() {
occluders.clear();
physics.clear();
@@ -3628,11 +4538,11 @@ Vector2i TileData::get_texture_offset() const {
return tex_offset;
}
-void TileData::tile_set_material(Ref<ShaderMaterial> p_material) {
+void TileData::set_material(Ref<ShaderMaterial> p_material) {
material = p_material;
emit_signal(SNAME("changed"));
}
-Ref<ShaderMaterial> TileData::tile_get_material() const {
+Ref<ShaderMaterial> TileData::get_material() const {
return material;
}
@@ -3672,9 +4582,26 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const {
}
// Physics
-int TileData::get_collision_polygons_count(int p_layer_id) const {
- ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
- return physics[p_layer_id].polygons.size();
+void TileData::set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ physics.write[p_layer_id].linear_velocity = p_velocity;
+ emit_signal(SNAME("changed"));
+}
+
+Vector2 TileData::get_constant_linear_velocity(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector2());
+ return physics[p_layer_id].linear_velocity;
+}
+
+void TileData::set_constant_angular_velocity(int p_layer_id, real_t p_velocity) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ physics.write[p_layer_id].angular_velocity = p_velocity;
+ emit_signal(SNAME("changed"));
+}
+
+real_t TileData::get_constant_angular_velocity(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0);
+ return physics[p_layer_id].angular_velocity;
}
void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) {
@@ -3685,6 +4612,11 @@ void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count
emit_signal(SNAME("changed"));
}
+int TileData::get_collision_polygons_count(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ return physics[p_layer_id].polygons.size();
+}
+
void TileData::add_collision_polygon(int p_layer_id) {
ERR_FAIL_INDEX(p_layer_id, physics.size());
physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData());
@@ -3886,11 +4818,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
// Physics layers.
int layer_index = components[0].trim_prefix("physics_layer_").to_int();
ERR_FAIL_COND_V(layer_index < 0, false);
- if (components.size() == 2 && components[1] == "polygons_count") {
- if (p_value.get_type() != Variant::INT) {
- return false;
- }
-
+ if (components.size() == 2) {
if (layer_index >= physics.size()) {
if (tile_set) {
return false;
@@ -3898,8 +4826,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
physics.resize(layer_index + 1);
}
}
- set_collision_polygons_count(layer_index, p_value);
- return true;
+ if (components[1] == "linear_velocity") {
+ set_constant_linear_velocity(layer_index, p_value);
+ return true;
+ } else if (components[1] == "angular_velocity") {
+ set_constant_angular_velocity(layer_index, p_value);
+ return true;
+ } else if (components[1] == "polygons_count") {
+ if (p_value.get_type() != Variant::INT) {
+ return false;
+ }
+ set_collision_polygons_count(layer_index, p_value);
+ return true;
+ }
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
@@ -4001,9 +4940,18 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
if (layer_index >= physics.size()) {
return false;
}
- if (components.size() == 2 && components[1] == "polygons_count") {
- r_ret = get_collision_polygons_count(layer_index);
- return true;
+
+ if (components.size() == 2) {
+ if (components[1] == "linear_velocity") {
+ r_ret = get_constant_linear_velocity(layer_index);
+ return true;
+ } else if (components[1] == "angular_velocity") {
+ r_ret = get_constant_angular_velocity(layer_index);
+ return true;
+ } else if (components[1] == "polygons_count") {
+ r_ret = get_collision_polygons_count(layer_index);
+ return true;
+ }
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
@@ -4074,6 +5022,8 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
// Physics layers.
p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int i = 0; i < physics.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, vformat("physics_layer_%d/linear_velocity", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/angular_velocity", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
for (int j = 0; j < physics[i].polygons.size(); j++) {
@@ -4148,8 +5098,8 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v);
ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose);
ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose);
- ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material);
- ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material);
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &TileData::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &TileData::get_material);
ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset);
ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset);
ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate);
@@ -4163,8 +5113,12 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder);
// Physics.
- ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
+ ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_linear_velocity", "layer_id"), &TileData::get_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "layer_id", "velocity"), &TileData::set_constant_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_angular_velocity", "layer_id"), &TileData::get_constant_angular_velocity);
ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count);
+ ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon);
ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon);
ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points);
@@ -4200,6 +5154,7 @@ void TileData::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index");
ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 35e6999d13..530c90920f 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -60,6 +60,84 @@ class TileSetPluginAtlasRendering;
class TileSetPluginAtlasPhysics;
class TileSetPluginAtlasNavigation;
+union TileMapCell {
+ struct {
+ int32_t source_id : 16;
+ int16_t coord_x : 16;
+ int16_t coord_y : 16;
+ int32_t alternative_tile : 16;
+ };
+
+ uint64_t _u64t;
+ TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = Vector2i(-1, -1), int p_alternative_tile = -1) { // default are INVALID_SOURCE, INVALID_ATLAS_COORDS, INVALID_TILE_ALTERNATIVE
+ source_id = p_source_id;
+ set_atlas_coords(p_atlas_coords);
+ alternative_tile = p_alternative_tile;
+ }
+
+ Vector2i get_atlas_coords() const {
+ return Vector2i(coord_x, coord_y);
+ }
+
+ void set_atlas_coords(const Vector2i &r_coords) {
+ coord_x = r_coords.x;
+ coord_y = r_coords.y;
+ }
+
+ bool operator<(const TileMapCell &p_other) const {
+ if (source_id == p_other.source_id) {
+ if (coord_x == p_other.coord_x) {
+ if (coord_y == p_other.coord_y) {
+ return alternative_tile < p_other.alternative_tile;
+ } else {
+ return coord_y < p_other.coord_y;
+ }
+ } else {
+ return coord_x < p_other.coord_x;
+ }
+ } else {
+ return source_id < p_other.source_id;
+ }
+ }
+
+ bool operator!=(const TileMapCell &p_other) const {
+ return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile);
+ }
+};
+
+class TileMapPattern : public Resource {
+ GDCLASS(TileMapPattern, Resource);
+
+ Vector2i size;
+ Map<Vector2i, TileMapCell> pattern;
+
+ void _set_tile_data(const Vector<int> &p_data);
+ Vector<int> _get_tile_data() const;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+ void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0);
+ bool has_cell(const Vector2i &p_coords) const;
+ void remove_cell(const Vector2i &p_coords, bool p_update_size = true);
+ int get_cell_source_id(const Vector2i &p_coords) const;
+ Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
+ int get_cell_alternative_tile(const Vector2i &p_coords) const;
+
+ TypedArray<Vector2i> get_used_cells() const;
+
+ Vector2i get_size() const;
+ void set_size(const Vector2i &p_size);
+ bool is_empty() const;
+
+ void clear();
+};
+
class TileSet : public Resource {
GDCLASS(TileSet, Resource);
@@ -180,6 +258,7 @@ 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;
+ virtual void _validate_property(PropertyInfo &property) const override;
private:
// --- TileSet data ---
@@ -225,10 +304,10 @@ private:
bool terrain_bits_meshes_dirty = true;
// Navigation
- struct Navigationlayer {
+ struct NavigationLayer {
uint32_t layers = 1;
};
- Vector<Navigationlayer> navigation_layers;
+ Vector<NavigationLayer> navigation_layers;
// CustomData
struct CustomDataLayer {
@@ -244,6 +323,8 @@ private:
int next_source_id = 0;
// ---------------------
+ LocalVector<Ref<TileMapPattern>> patterns;
+
void _compute_next_source_id();
void _source_changed();
@@ -298,16 +379,20 @@ public:
void set_uv_clipping(bool p_uv_clipping);
bool is_uv_clipping() const;
- void set_occlusion_layers_count(int p_occlusion_layers_count);
int get_occlusion_layers_count() const;
+ void add_occlusion_layer(int p_index = -1);
+ void move_occlusion_layer(int p_from_index, int p_to_pos);
+ void remove_occlusion_layer(int p_index);
void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
int get_occlusion_layer_light_mask(int p_layer_index) const;
- void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision);
+ void set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision);
bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
// Physics
- void set_physics_layers_count(int p_physics_layers_count);
int get_physics_layers_count() const;
+ void add_physics_layer(int p_index = -1);
+ void move_physics_layer(int p_from_index, int p_to_pos);
+ void remove_physics_layer(int p_index);
void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
@@ -315,13 +400,19 @@ public:
void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
- // Terrains
- void set_terrain_sets_count(int p_terrains_sets_count);
+ // Terrain sets
int get_terrain_sets_count() const;
+ void add_terrain_set(int p_index = -1);
+ void move_terrain_set(int p_from_index, int p_to_pos);
+ void remove_terrain_set(int p_index);
void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
TerrainMode get_terrain_set_mode(int p_terrain_set) const;
- void set_terrains_count(int p_terrain_set, int p_terrains_count);
+
+ // Terrains
int get_terrains_count(int p_terrain_set) const;
+ void add_terrain(int p_terrain_set, int p_index = -1);
+ void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
+ void remove_terrain(int p_terrain_set, int p_index);
void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
@@ -330,14 +421,18 @@ public:
bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
// Navigation
- void set_navigation_layers_count(int p_navigation_layers_count);
int get_navigation_layers_count() const;
+ void add_navigation_layer(int p_index = -1);
+ void move_navigation_layer(int p_from_index, int p_to_pos);
+ void remove_navigation_layer(int p_index);
void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
uint32_t get_navigation_layer_layers(int p_layer_index) const;
// Custom data
- void set_custom_data_layers_count(int p_custom_data_layers_count);
int get_custom_data_layers_count() const;
+ void add_custom_data_layer(int p_index = -1);
+ void move_custom_data_layer(int p_from_index, int p_to_pos);
+ void remove_custom_data_layer(int p_index);
int get_custom_data_layer_by_name(String p_value) const;
void set_custom_data_name(int p_layer_id, String p_value);
String get_custom_data_name(int p_layer_id) const;
@@ -369,9 +464,15 @@ public:
void cleanup_invalid_tile_proxies();
void clear_tile_proxies();
+ // Patterns.
+ int add_pattern(Ref<TileMapPattern> p_pattern, int p_index = -1);
+ Ref<TileMapPattern> get_pattern(int p_index);
+ void remove_pattern(int p_index);
+ int get_patterns_count();
+
// Helpers
Vector<Vector2> get_tile_shape_polygon();
- void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
+ void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
@@ -390,6 +491,8 @@ class TileSetSource : public Resource {
protected:
const TileSet *tile_set = nullptr;
+ static void _bind_methods();
+
public:
static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
static const int INVALID_TILE_ALTERNATIVE; // -1;
@@ -397,6 +500,24 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
virtual void notify_tile_data_properties_should_change(){};
+ virtual void add_occlusion_layer(int p_index){};
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_occlusion_layer(int p_index){};
+ virtual void add_physics_layer(int p_index){};
+ virtual void move_physics_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_physics_layer(int p_index){};
+ virtual void add_terrain_set(int p_index){};
+ virtual void move_terrain_set(int p_from_index, int p_to_pos){};
+ virtual void remove_terrain_set(int p_index){};
+ virtual void add_terrain(int p_terrain_set, int p_index){};
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
+ virtual void remove_terrain(int p_terrain_set, int p_index){};
+ virtual void add_navigation_layer(int p_index){};
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_navigation_layer(int p_index){};
+ virtual void add_custom_data_layer(int p_index){};
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
+ virtual void remove_custom_data_layer(int p_index){};
virtual void reset_state() override{};
// Tiles.
@@ -413,16 +534,23 @@ public:
class TileSetAtlasSource : public TileSetSource {
GDCLASS(TileSetAtlasSource, TileSetSource);
-public:
+private:
struct TileAlternativesData {
Vector2i size_in_atlas = Vector2i(1, 1);
Vector2i texture_offset;
+
+ // Animation
+ int animation_columns = 0;
+ Vector2i animation_separation;
+ real_t animation_speed = 1.0;
+ LocalVector<real_t> animation_frames_durations;
+
+ // Alternatives
Map<int, TileData *> alternatives;
Vector<int> alternatives_ids;
int next_alternative_id = 1;
};
-private:
Ref<Texture2D> texture;
Vector2i margins;
Vector2i separation;
@@ -437,6 +565,11 @@ private:
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
+ void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
+ void _create_coords_mapping_cache(Vector2i p_atlas_coords);
+
+ void _clear_tiles_outside_texture();
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -448,6 +581,24 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
virtual void notify_tile_data_properties_should_change() override;
+ virtual void add_occlusion_layer(int p_index) override;
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_occlusion_layer(int p_index) override;
+ virtual void add_physics_layer(int p_index) override;
+ virtual void move_physics_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_physics_layer(int p_index) override;
+ virtual void add_terrain_set(int p_index) override;
+ virtual void move_terrain_set(int p_from_index, int p_to_pos) override;
+ virtual void remove_terrain_set(int p_index) override;
+ virtual void add_terrain(int p_terrain_set, int p_index) override;
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) override;
+ virtual void remove_terrain(int p_terrain_set, int p_index) override;
+ virtual void add_navigation_layer(int p_index) override;
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_navigation_layer(int p_index) override;
+ virtual void add_custom_data_layer(int p_index) override;
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos) override;
+ virtual void remove_custom_data_layer(int p_index) override;
virtual void reset_state() override;
// Base properties.
@@ -461,18 +612,32 @@ public:
Vector2i get_texture_region_size() const;
// Base tiles.
- void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does.
- void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
+ void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
+ void remove_tile(Vector2i p_atlas_coords);
virtual bool has_tile(Vector2i p_atlas_coords) const override;
- bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const;
void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
virtual int get_tiles_count() const override;
virtual Vector2i get_tile_id(int p_index) const override;
+ bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
+ PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
+ // Animation.
+ void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns);
+ int get_tile_animation_columns(const Vector2i p_atlas_coords) const;
+ void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation);
+ Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const;
+ void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed);
+ real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const;
+ void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count);
+ int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const;
+ void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration);
+ real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const;
+ real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const;
+
// Alternative tiles.
int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
@@ -488,9 +653,7 @@ public:
// Helpers.
Vector2i get_atlas_grid_size() const;
- bool has_tiles_outside_texture();
- void clear_tiles_outside_texture();
- Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const;
+ Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
~TileSetAtlasSource();
@@ -528,7 +691,7 @@ public:
int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
- // Scenes sccessors. Lot are similar to "Alternative tiles".
+ // Scenes accessors. Lot are similar to "Alternative tiles".
int get_scene_tiles_count() { return get_alternative_tiles_count(Vector2i()); }
int get_scene_tile_id(int p_index) { return get_alternative_tile_id(Vector2i(), p_index); };
bool has_scene_tile_id(int p_id) { return has_alternative_tile(Vector2i(), p_id); };
@@ -569,6 +732,8 @@ private:
float one_way_margin = 1.0;
};
+ Vector2 linear_velocity;
+ float angular_velocity = 0.0;
Vector<PolygonShapeTileData> polygons;
};
Vector<PhysicsLayerTileData> physics;
@@ -597,6 +762,24 @@ public:
// Not exposed.
void set_tile_set(const TileSet *p_tile_set);
void notify_tile_data_properties_should_change();
+ void add_occlusion_layer(int p_index);
+ void move_occlusion_layer(int p_from_index, int p_to_pos);
+ void remove_occlusion_layer(int p_index);
+ void add_physics_layer(int p_index);
+ void move_physics_layer(int p_from_index, int p_to_pos);
+ void remove_physics_layer(int p_index);
+ void add_terrain_set(int p_index);
+ void move_terrain_set(int p_from_index, int p_to_pos);
+ void remove_terrain_set(int p_index);
+ void add_terrain(int p_terrain_set, int p_index);
+ void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos);
+ void remove_terrain(int p_terrain_set, int p_index);
+ void add_navigation_layer(int p_index);
+ void move_navigation_layer(int p_from_index, int p_to_pos);
+ void remove_navigation_layer(int p_index);
+ void add_custom_data_layer(int p_index);
+ void move_custom_data_layer(int p_from_index, int p_to_pos);
+ void remove_custom_data_layer(int p_index);
void reset_state();
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
@@ -611,8 +794,8 @@ public:
void set_texture_offset(Vector2i p_texture_offset);
Vector2i get_texture_offset() const;
- void tile_set_material(Ref<ShaderMaterial> p_material);
- Ref<ShaderMaterial> tile_get_material() const;
+ void set_material(Ref<ShaderMaterial> p_material);
+ Ref<ShaderMaterial> get_material() const;
void set_modulate(Color p_modulate);
Color get_modulate() const;
void set_z_index(int p_z_index);
@@ -624,8 +807,12 @@ public:
Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
// Physics
- int get_collision_polygons_count(int p_layer_id) const;
+ void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
+ Vector2 get_constant_linear_velocity(int p_layer_id) const;
+ void set_constant_angular_velocity(int p_layer_id, real_t p_velocity);
+ real_t get_constant_angular_velocity(int p_layer_id) const;
void set_collision_polygons_count(int p_layer_id, int p_shapes_count);
+ int get_collision_polygons_count(int p_layer_id) const;
void add_collision_polygon(int p_layer_id);
void remove_collision_polygon(int p_layer_id, int p_polygon_index);
void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index e8fe3ff3cd..0dc83b4bb8 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -113,6 +113,10 @@ bool VisualShaderNode::is_output_port_expandable(int p_port) const {
return false;
}
+bool VisualShaderNode::has_output_port_preview(int p_port) const {
+ return true;
+}
+
void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) {
for (int i = 0; i < p_values.size(); i++) {
expanded_output_ports[p_values[i]] = true;
@@ -197,9 +201,9 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const {
Array VisualShaderNode::get_default_input_values() const {
Array ret;
- for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) {
- ret.push_back(E->key());
- ret.push_back(E->get());
+ for (const KeyValue<int, Variant> &E : default_input_values) {
+ ret.push_back(E.key);
+ ret.push_back(E.value);
}
return ret;
}
@@ -460,14 +464,14 @@ Dictionary VisualShader::get_engine_version() const {
void VisualShader::update_engine_version(const Dictionary &p_new_version) {
if (engine_version.is_empty()) { // before 4.0
for (int i = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E->get().node.ptr());
+ for (KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E.value.node.ptr());
if (input.is_valid()) {
if (input->get_input_name() == "side") {
input->set_input_name("front_facing");
}
}
- Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr());
+ Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr());
if (expression.is_valid()) {
for (int j = 0; j < expression->get_input_port_count(); j++) {
int type = expression->get_input_port_type(j);
@@ -484,7 +488,7 @@ void VisualShader::update_engine_version(const Dictionary &p_new_version) {
expression->set_output_port_type(j, type);
}
}
- Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E->get().node.ptr());
+ Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr());
if (compare.is_valid()) {
int ctype = int(compare->get_comparison_type());
if (int(ctype) > 0) { // + PORT_TYPE_SCALAR_INT
@@ -561,8 +565,8 @@ Vector<int> VisualShader::get_node_list(Type p_type) const {
const Graph *g = &graph[p_type];
Vector<int> ret;
- for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<int, Node> &E : g->nodes) {
+ ret.push_back(E.key);
}
return ret;
@@ -575,9 +579,9 @@ int VisualShader::get_valid_node_id(Type p_type) const {
}
int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const {
- for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) {
- if (E->get().node == p_node) {
- return E->key();
+ for (const KeyValue<int, Node> &E : graph[p_type].nodes) {
+ if (E.value.node == p_node) {
+ return E.key;
}
}
@@ -819,8 +823,8 @@ void VisualShader::set_mode(Mode p_mode) {
flags.clear();
shader_mode = p_mode;
for (int i = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeInput> input = E->get().node;
+ for (KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeInput> input = E.value.node;
if (input.is_valid()) {
input->shader_mode = shader_mode;
//input->input_index = 0;
@@ -1041,8 +1045,8 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua
while (true) {
bool exists = false;
for (int i = 0; i < TYPE_MAX; i++) {
- for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeUniform> node = E->get().node;
+ for (const KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeUniform> node = E.value.node;
if (node == p_uniform) { //do not test on self
continue;
}
@@ -1271,8 +1275,8 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
- for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) {
- p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get()));
+ for (const KeyValue<String, String> &E : blend_mode_enums) {
+ p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value));
}
for (Set<String>::Element *E = toggles.front(); E; E = E->next()) {
@@ -1280,22 +1284,22 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (int i = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+ for (const KeyValue<int, Node> &E : graph[i].nodes) {
String prop_name = "nodes/";
prop_name += type_string[i];
- prop_name += "/" + itos(E->key());
+ prop_name += "/" + itos(E.key);
- if (E->key() != NODE_ID_OUTPUT) {
+ if (E.key != NODE_ID_OUTPUT) {
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
}
p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != nullptr) {
+ if (Object::cast_to<VisualShaderNodeGroupBase>(E.value.node.ptr()) != nullptr) {
p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
- if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != nullptr) {
+ if (Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()) != nullptr) {
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
}
@@ -1716,9 +1720,9 @@ void VisualShader::_update_shader() const {
emitters.insert(i, List<int>());
}
- for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) {
- if (M->get().node == emit_particle.ptr()) {
- emitters[i].push_back(M->key());
+ for (const KeyValue<int, Node> &M : graph[i].nodes) {
+ if (M.value.node == emit_particle.ptr()) {
+ emitters[i].push_back(M.key);
break;
}
}
@@ -2307,7 +2311,21 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
};
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
+ // Spatial, Vertex
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Spatial, Fragment
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
@@ -2315,44 +2333,59 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Vertex
+
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Fragment
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Light
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
-
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Sky
+
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Particles
+
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2413,13 +2446,10 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_VECTOR: {
code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
- case PORT_TYPE_TRANSFORM: {
- code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
- } break;
case PORT_TYPE_BOOLEAN: {
code = " " + p_output_vars[0] + " = false;\n";
} break;
- default: //default (none found) is scalar
+ default:
break;
}
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index b3efac02aa..f910f28536 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -254,6 +254,8 @@ public:
void set_input_port_connected(int p_port, bool p_connected);
virtual bool is_generate_input_var(int p_port) const;
+ virtual bool has_output_port_preview(int p_port) const;
+
virtual bool is_output_port_expandable(int p_port) const;
void _set_output_ports_expanded(const Array &p_data);
Array _get_output_ports_expanded() const;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index e45dfdcb1b..c3d9ef7b04 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -918,6 +918,7 @@ bool VisualShaderNodeCurveTexture::is_use_prop_slots() const {
}
VisualShaderNodeCurveTexture::VisualShaderNodeCurveTexture() {
+ set_input_port_default_value(0, 0.0);
simple_decl = true;
allow_v_resize = false;
}
@@ -1002,6 +1003,7 @@ bool VisualShaderNodeCurveXYZTexture::is_use_prop_slots() const {
}
VisualShaderNodeCurveXYZTexture::VisualShaderNodeCurveXYZTexture() {
+ set_input_port_default_value(0, 0.0);
simple_decl = true;
allow_v_resize = false;
}
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 5fe801e037..18b933e5cf 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -47,6 +47,10 @@ String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
return String();
}
+bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
}
@@ -265,6 +269,10 @@ Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_pro
return props;
}
+bool VisualShaderNodeParticleMultiplyByAxisAngle::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() {
set_input_port_default_value(1, Vector3(1, 0, 0));
set_input_port_default_value(2, 0.0);
@@ -313,6 +321,10 @@ String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) co
return String();
}
+bool VisualShaderNodeParticleConeVelocity::has_output_port_preview(int p_port) const {
+ return false;
+}
+
String VisualShaderNodeParticleConeVelocity::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
@@ -421,6 +433,10 @@ VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::g
return op_type;
}
+bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 1.0);
@@ -521,6 +537,10 @@ VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::g
return mode;
}
+bool VisualShaderNodeParticleAccelerator::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() {
set_input_port_default_value(0, Vector3(1, 1, 1));
set_input_port_default_value(1, 0.0);
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index f5435c3511..b8bc7992cc 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -42,6 +42,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
VisualShaderNodeParticleEmitter();
};
@@ -112,6 +113,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -135,6 +137,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -168,6 +171,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -209,6 +213,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
diff --git a/scene/resources/world_margin_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp
index 3b43681528..39af92793f 100644
--- a/scene/resources/world_margin_shape_2d.cpp
+++ b/scene/resources/world_boundary_shape_2d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* world_margin_shape_2d.cpp */
+/* world_boundary_shape_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "world_margin_shape_2d.h"
+#include "world_boundary_shape_2d.h"
#include "core/math/geometry_2d.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
-bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+bool WorldBoundaryShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
Vector2 point = get_distance() * get_normal();
Vector2 l[2][2] = { { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }, { point, point + get_normal() * 30 } };
@@ -48,7 +48,7 @@ bool WorldMarginShape2D::_edit_is_selected_on_click(const Point2 &p_point, doubl
return false;
}
-void WorldMarginShape2D::_update_shape() {
+void WorldBoundaryShape2D::_update_shape() {
Array arr;
arr.push_back(normal);
arr.push_back(distance);
@@ -56,25 +56,25 @@ void WorldMarginShape2D::_update_shape() {
emit_changed();
}
-void WorldMarginShape2D::set_normal(const Vector2 &p_normal) {
+void WorldBoundaryShape2D::set_normal(const Vector2 &p_normal) {
normal = p_normal;
_update_shape();
}
-void WorldMarginShape2D::set_distance(real_t p_distance) {
+void WorldBoundaryShape2D::set_distance(real_t p_distance) {
distance = p_distance;
_update_shape();
}
-Vector2 WorldMarginShape2D::get_normal() const {
+Vector2 WorldBoundaryShape2D::get_normal() const {
return normal;
}
-real_t WorldMarginShape2D::get_distance() const {
+real_t WorldBoundaryShape2D::get_distance() const {
return distance;
}
-void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) {
+void WorldBoundaryShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -83,7 +83,7 @@ void WorldMarginShape2D::draw(const RID &p_to_rid, const Color &p_color) {
RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3);
}
-Rect2 WorldMarginShape2D::get_rect() const {
+Rect2 WorldBoundaryShape2D::get_rect() const {
Vector2 point = get_distance() * get_normal();
Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 };
@@ -96,22 +96,22 @@ Rect2 WorldMarginShape2D::get_rect() const {
return rect;
}
-real_t WorldMarginShape2D::get_enclosing_radius() const {
+real_t WorldBoundaryShape2D::get_enclosing_radius() const {
return distance;
}
-void WorldMarginShape2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldMarginShape2D::set_normal);
- ClassDB::bind_method(D_METHOD("get_normal"), &WorldMarginShape2D::get_normal);
+void WorldBoundaryShape2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_normal", "normal"), &WorldBoundaryShape2D::set_normal);
+ ClassDB::bind_method(D_METHOD("get_normal"), &WorldBoundaryShape2D::get_normal);
- ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldMarginShape2D::set_distance);
- ClassDB::bind_method(D_METHOD("get_distance"), &WorldMarginShape2D::get_distance);
+ ClassDB::bind_method(D_METHOD("set_distance", "distance"), &WorldBoundaryShape2D::set_distance);
+ ClassDB::bind_method(D_METHOD("get_distance"), &WorldBoundaryShape2D::get_distance);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "set_normal", "get_normal");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance");
}
-WorldMarginShape2D::WorldMarginShape2D() :
- Shape2D(PhysicsServer2D::get_singleton()->world_margin_shape_create()) {
+WorldBoundaryShape2D::WorldBoundaryShape2D() :
+ Shape2D(PhysicsServer2D::get_singleton()->world_boundary_shape_create()) {
_update_shape();
}
diff --git a/scene/resources/world_margin_shape_2d.h b/scene/resources/world_boundary_shape_2d.h
index 3c1d593ffe..4cc60f5985 100644
--- a/scene/resources/world_margin_shape_2d.h
+++ b/scene/resources/world_boundary_shape_2d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* world_margin_shape_2d.h */
+/* world_boundary_shape_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WORLD_MARGIN_SHAPE_2D_H
-#define WORLD_MARGIN_SHAPE_2D_H
+#ifndef WORLD_BOUNDARY_SHAPE_2D_H
+#define WORLD_BOUNDARY_SHAPE_2D_H
#include "scene/resources/shape_2d.h"
-class WorldMarginShape2D : public Shape2D {
- GDCLASS(WorldMarginShape2D, Shape2D);
+class WorldBoundaryShape2D : public Shape2D {
+ GDCLASS(WorldBoundaryShape2D, Shape2D);
- // WorldMarginShape2D is often used for one-way platforms, where the normal pointing up makes sense.
+ // WorldBoundaryShape2D is often used for one-way platforms, where the normal pointing up makes sense.
Vector2 normal = Vector2(0, -1);
real_t distance = 0.0;
@@ -58,7 +58,7 @@ public:
virtual Rect2 get_rect() const override;
virtual real_t get_enclosing_radius() const override;
- WorldMarginShape2D();
+ WorldBoundaryShape2D();
};
-#endif // WORLD_MARGIN_SHAPE_2D_H
+#endif // WORLD_BOUNDARY_SHAPE_2D_H
diff --git a/scene/resources/world_margin_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp
index 28d50e1921..8cde537164 100644
--- a/scene/resources/world_margin_shape_3d.cpp
+++ b/scene/resources/world_boundary_shape_3d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* world_margin_shape_3d.cpp */
+/* world_boundary_shape_3d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "world_margin_shape_3d.h"
+#include "world_boundary_shape_3d.h"
#include "servers/physics_server_3d.h"
-Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const {
+Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const {
Plane p = get_plane();
Vector<Vector3> points;
@@ -60,29 +60,29 @@ Vector<Vector3> WorldMarginShape3D::get_debug_mesh_lines() const {
return points;
}
-void WorldMarginShape3D::_update_shape() {
+void WorldBoundaryShape3D::_update_shape() {
PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), plane);
Shape3D::_update_shape();
}
-void WorldMarginShape3D::set_plane(Plane p_plane) {
+void WorldBoundaryShape3D::set_plane(const Plane &p_plane) {
plane = p_plane;
_update_shape();
notify_change_to_owners();
}
-Plane WorldMarginShape3D::get_plane() const {
+const Plane &WorldBoundaryShape3D::get_plane() const {
return plane;
}
-void WorldMarginShape3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldMarginShape3D::set_plane);
- ClassDB::bind_method(D_METHOD("get_plane"), &WorldMarginShape3D::get_plane);
+void WorldBoundaryShape3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_plane", "plane"), &WorldBoundaryShape3D::set_plane);
+ ClassDB::bind_method(D_METHOD("get_plane"), &WorldBoundaryShape3D::get_plane);
ADD_PROPERTY(PropertyInfo(Variant::PLANE, "plane"), "set_plane", "get_plane");
}
-WorldMarginShape3D::WorldMarginShape3D() :
- Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_PLANE)) {
+WorldBoundaryShape3D::WorldBoundaryShape3D() :
+ Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_WORLD_BOUNDARY)) {
set_plane(Plane(0, 1, 0, 0));
}
diff --git a/scene/resources/world_margin_shape_3d.h b/scene/resources/world_boundary_shape_3d.h
index 00417c4408..853f555ebc 100644
--- a/scene/resources/world_margin_shape_3d.h
+++ b/scene/resources/world_boundary_shape_3d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* world_margin_shape_3d.h */
+/* world_boundary_shape_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WORLD_MARGIN_SHAPE_3D_H
-#define WORLD_MARGIN_SHAPE_3D_H
+#ifndef WORLD_BOUNDARY_SHAPE_3D_H
+#define WORLD_BOUNDARY_SHAPE_3D_H
#include "scene/resources/shape_3d.h"
-class WorldMarginShape3D : public Shape3D {
- GDCLASS(WorldMarginShape3D, Shape3D);
+class WorldBoundaryShape3D : public Shape3D {
+ GDCLASS(WorldBoundaryShape3D, Shape3D);
Plane plane;
protected:
@@ -42,8 +42,8 @@ protected:
virtual void _update_shape() override;
public:
- void set_plane(Plane p_plane);
- Plane get_plane() const;
+ void set_plane(const Plane &p_plane);
+ const Plane &get_plane() const;
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override {
@@ -51,6 +51,6 @@ public:
return 0;
}
- WorldMarginShape3D();
+ WorldBoundaryShape3D();
};
-#endif // WORLD_MARGIN_SHAPE_H
+#endif // WORLD_BOUNDARY_SHAPE_H