summaryrefslogtreecommitdiff
path: root/scene/resources
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources')
-rw-r--r--scene/resources/animation.cpp3394
-rw-r--r--scene/resources/animation.h202
-rw-r--r--scene/resources/audio_stream_sample.cpp8
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/bit_map.cpp2
-rw-r--r--scene/resources/camera_effects.cpp2
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp2
-rw-r--r--scene/resources/curve.cpp6
-rw-r--r--scene/resources/default_theme/SCsub2
-rw-r--r--scene/resources/default_theme/default_theme.cpp50
-rw-r--r--scene/resources/default_theme/overbright_indicator.pngbin593 -> 210 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h10
-rw-r--r--scene/resources/environment.cpp93
-rw-r--r--scene/resources/environment.h31
-rw-r--r--scene/resources/fog_material.cpp181
-rw-r--r--scene/resources/fog_material.h87
-rw-r--r--scene/resources/font.cpp158
-rw-r--r--scene/resources/font.h37
-rw-r--r--scene/resources/gradient.cpp30
-rw-r--r--scene/resources/gradient.h57
-rw-r--r--scene/resources/importer_mesh.cpp8
-rw-r--r--scene/resources/material.cpp16
-rw-r--r--scene/resources/mesh.cpp6
-rw-r--r--scene/resources/mesh.h1
-rw-r--r--scene/resources/mesh_data_tool.cpp2
-rw-r--r--scene/resources/navigation_mesh.cpp4
-rw-r--r--scene/resources/navigation_mesh.h2
-rw-r--r--scene/resources/packed_scene.cpp218
-rw-r--r--scene/resources/packed_scene.h16
-rw-r--r--scene/resources/particles_material.cpp2
-rw-r--r--scene/resources/polygon_path_finder.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp6
-rw-r--r--scene/resources/shader.cpp39
-rw-r--r--scene/resources/shader.h7
-rw-r--r--scene/resources/shape_2d.h1
-rw-r--r--scene/resources/skeleton_modification_2d_jiggle.cpp8
-rw-r--r--scene/resources/skeleton_modification_2d_lookat.cpp2
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp78
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h2
-rw-r--r--scene/resources/skeleton_modification_3d_jiggle.cpp15
-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_3d.cpp1
-rw-r--r--scene/resources/skin.cpp2
-rw-r--r--scene/resources/sprite_frames.cpp2
-rw-r--r--scene/resources/style_box.cpp2
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/text_paragraph.cpp18
-rw-r--r--scene/resources/text_paragraph.h5
-rw-r--r--scene/resources/texture.cpp305
-rw-r--r--scene/resources/texture.h82
-rw-r--r--scene/resources/theme.cpp4
-rw-r--r--scene/resources/tile_set.cpp772
-rw-r--r--scene/resources/tile_set.h149
-rw-r--r--scene/resources/visual_shader.cpp149
-rw-r--r--scene/resources/visual_shader.h12
-rw-r--r--scene/resources/visual_shader_nodes.cpp19
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp381
-rw-r--r--scene/resources/visual_shader_particle_nodes.h61
-rw-r--r--scene/resources/world_3d.cpp4
60 files changed, 5643 insertions, 1148 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index b4eec2530b..3700e87839 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -30,21 +30,54 @@
#include "animation.h"
+#include "core/io/marshalls.h"
#include "core/math/geometry_3d.h"
#include "scene/scene_string_names.h"
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
- if (name.begins_with("tracks/")) {
+ if (p_name == SNAME("_compression")) {
+ ERR_FAIL_COND_V(tracks.size() > 0, false); //can only set compression if no tracks exist
+ Dictionary comp = p_value;
+ ERR_FAIL_COND_V(!comp.has("fps"), false);
+ ERR_FAIL_COND_V(!comp.has("bounds"), false);
+ ERR_FAIL_COND_V(!comp.has("pages"), false);
+ ERR_FAIL_COND_V(!comp.has("format_version"), false);
+ uint32_t format_version = comp["format_version"];
+ ERR_FAIL_COND_V(format_version > Compression::FORMAT_VERSION, false); // version does not match this supported version
+ compression.fps = comp["fps"];
+ Array bounds = comp["bounds"];
+ compression.bounds.resize(bounds.size());
+ for (int i = 0; i < bounds.size(); i++) {
+ compression.bounds[i] = bounds[i];
+ }
+ Array pages = comp["pages"];
+ compression.pages.resize(pages.size());
+ for (int i = 0; i < pages.size(); i++) {
+ Dictionary page = pages[i];
+ ERR_FAIL_COND_V(!page.has("data"), false);
+ ERR_FAIL_COND_V(!page.has("time_offset"), false);
+ compression.pages[i].data = page["data"];
+ compression.pages[i].time_offset = page["time_offset"];
+ }
+ compression.enabled = true;
+ return true;
+ } else if (name.begins_with("tracks/")) {
int track = name.get_slicec('/', 1).to_int();
String what = name.get_slicec('/', 2);
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") {
@@ -66,6 +99,34 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (what == "path") {
track_set_path(track, p_value);
+ } else if (what == "compressed_track") {
+ int index = p_value;
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)index, compression.bounds.size(), false);
+ Track *t = tracks[track];
+ t->interpolation = INTERPOLATION_LINEAR; //only linear supported
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ tt->compressed_track = index;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ rt->compressed_track = index;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ st->compressed_track = index;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ bst->compressed_track = index;
+ } break;
+ default: {
+ return false;
+ }
+ }
+ return true;
} else if (what == "interp") {
track_set_interpolation_type(track, InterpolationType(p_value.operator int()));
} else if (what == "loop_wrap") {
@@ -75,35 +136,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();
- tk.value.rot.x = ofs[5];
- tk.value.rot.y = ofs[6];
- tk.value.rot.z = ofs[7];
- tk.value.rot.w = ofs[8];
+ int64_t count = vcount / ROTATION_TRACK_SIZE;
+ rt->rotations.resize(count);
- tk.value.scale.x = ofs[9];
- tk.value.scale.y = ofs[10];
- tk.value.scale.z = ofs[11];
+ 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);
+
+ 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);
+
+ 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) {
@@ -307,10 +424,33 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
- if (name == "length") {
+ if (p_name == SNAME("_compression")) {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ Dictionary comp;
+ comp["fps"] = compression.fps;
+ Array bounds;
+ bounds.resize(compression.bounds.size());
+ for (uint32_t i = 0; i < compression.bounds.size(); i++) {
+ bounds[i] = compression.bounds[i];
+ }
+ comp["bounds"] = bounds;
+ Array pages;
+ pages.resize(compression.pages.size());
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ Dictionary page;
+ page["data"] = compression.pages[i].data;
+ page["time_offset"] = compression.pages[i].time_offset;
+ pages[i] = page;
+ }
+ comp["pages"] = pages;
+ comp["format_version"] = Compression::FORMAT_VERSION;
+
+ r_ret = comp;
+ return true;
+ } else if (name == "length") {
r_ret = length;
- } else if (name == "loop") {
- r_ret = loop;
+ } else if (name == "loop_mode") {
+ r_ret = loop_mode;
} else if (name == "step") {
r_ret = step;
} else if (name.begins_with("tracks/")) {
@@ -319,8 +459,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";
@@ -343,6 +492,34 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "path") {
r_ret = track_get_path(track);
+ } else if (what == "compressed_track") {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ Track *t = tracks[track];
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ r_ret = tt->compressed_track;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ r_ret = rt->compressed_track;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ r_ret = st->compressed_track;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ r_ret = bst->compressed_track;
+ } break;
+ default: {
+ r_ret = Variant();
+ ERR_FAIL_V(false);
+ }
+ }
+
+ return true;
+
} else if (what == "interp") {
r_ret = track_get_interpolation_type(track);
} else if (what == "loop_wrap") {
@@ -352,31 +529,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 +594,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]);
@@ -570,14 +798,21 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
}
void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (compression.enabled) {
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ }
for (int i = 0; i < tracks.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ if (track_is_compressed(i)) {
+ p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/compressed_track", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ }
}
}
@@ -591,10 +826,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 +876,28 @@ 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);
+ ERR_FAIL_COND_MSG(tt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
+ _clear(tt->positions);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_MSG(rt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
+ _clear(rt->rotations);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND_MSG(st->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
+ _clear(st->scales);
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND_MSG(bst->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
+ _clear(bst->blend_shapes);
} break;
case TYPE_VALUE: {
@@ -672,7 +938,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;
}
@@ -688,9 +954,9 @@ NodePath Animation::track_get_path(int p_track) const {
return tracks[p_track]->path;
}
-int Animation::find_track(const NodePath &p_path) const {
+int Animation::find_track(const NodePath &p_path, const TrackType p_type) const {
for (int i = 0; i < tracks.size(); i++) {
- if (tracks[i]->path == p_path) {
+ if (tracks[i]->path == p_path && tracks[i]->type == p_type) {
return i;
}
};
@@ -720,31 +986,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 +1015,292 @@ 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);
+
+ ERR_FAIL_COND_V(tt->compressed_track >= 0, -1);
+
+ 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];
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+
+ if (tt->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<3>(tt->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_position = _uncompress_pos_scale(tt->compressed_track, key);
+ return OK;
+ }
+
+ ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER);
+
+ *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);
+
+ if (tt->compressed_track >= 0) {
+ if (_pos_scale_interpolate_compressed(tt->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
+
+ bool ok = false;
+
+ Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *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);
+
+ ERR_FAIL_COND_V(rt->compressed_track >= 0, -1);
+
+ 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];
- 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);
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+
+ if (rt->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<3>(rt->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
- if (r_loc) {
- *r_loc = tt->transforms[p_key].value.loc;
+ *r_rotation = _uncompress_quaternion(key);
+ return OK;
}
- if (r_rot) {
- *r_rot = tt->transforms[p_key].value.rot;
+
+ 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);
+
+ if (rt->compressed_track >= 0) {
+ if (_rotation_interpolate_compressed(rt->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
+
+ 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);
+
+ ERR_FAIL_COND_V(st->compressed_track >= 0, -1);
+
+ 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);
+
+ if (st->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<3>(st->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_scale = _uncompress_pos_scale(st->compressed_track, key);
+ return OK;
}
- if (r_scale) {
- *r_scale = tt->transforms[p_key].value.scale;
+
+ 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);
+
+ if (st->compressed_track >= 0) {
+ if (_pos_scale_interpolate_compressed(st->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
}
+ 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);
+
+ BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
- TransformTrack *tt = static_cast<TransformTrack *>(t);
+ ERR_FAIL_COND_V(st->compressed_track >= 0, -1);
- 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 *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
+
+ if (bst->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<1>(bst->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_blend_shape = _uncompress_blend_shape(key);
+ return OK;
+ }
+
+ ERR_FAIL_INDEX_V(p_key, bst->blend_shapes.size(), ERR_INVALID_PARAMETER);
+
+ *r_blend_shape = bst->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 *bst = static_cast<BlendShapeTrack *>(t);
+
+ if (bst->compressed_track >= 0) {
+ if (_blend_shape_interpolate_compressed(bst->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
+
+ bool ok = false;
+
+ float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->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 +1312,40 @@ 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_COND(tt->compressed_track >= 0);
+
+ 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_COND(rt->compressed_track >= 0);
+
+ 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_COND(st->compressed_track >= 0);
+
+ 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_COND(bst->compressed_track >= 0);
+
+ ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size());
+ bst->blend_shapes.remove(p_idx);
} break;
case TYPE_VALUE: {
@@ -870,13 +1388,109 @@ 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);
+
+ if (tt->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
+ 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);
+
+ if (rt->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
+ 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);
+
+ if (st->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
+ int k = _find(st->scales, p_time);
+ if (k < 0 || k >= st->scales.size()) {
return -1;
}
- if (tt->transforms[k].time != p_time && p_exact) {
+ if (st->scales[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+
+ if (bst->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
+ int k = _find(bst->blend_shapes, p_time);
+ if (k < 0 || k >= bst->blend_shapes.size()) {
+ return -1;
+ }
+ if (bst->blend_shapes[k].time != p_time && p_exact) {
return -1;
}
return k;
@@ -952,24 +1566,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 +1671,33 @@ 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);
+ if (tt->compressed_track >= 0) {
+ return _get_compressed_key_count(tt->compressed_track);
+ }
+ return tt->positions.size();
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ return _get_compressed_key_count(rt->compressed_track);
+ }
+ return rt->rotations.size();
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ return _get_compressed_key_count(st->compressed_track);
+ }
+ return st->scales.size();
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ return _get_compressed_key_count(bst->compressed_track);
+ }
+ return bst->blend_shapes.size();
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1089,16 +1730,25 @@ 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());
-
- 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 d;
+ case TYPE_POSITION_3D: {
+ Vector3 value;
+ position_track_get_key(p_track, p_key_idx, &value);
+ return value;
+ } break;
+ case TYPE_ROTATION_3D: {
+ Quaternion value;
+ rotation_track_get_key(p_track, p_key_idx, &value);
+ return value;
+ } break;
+ case TYPE_SCALE_3D: {
+ Vector3 value;
+ scale_track_get_key(p_track, p_key_idx, &value);
+ return value;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ float value;
+ blend_shape_track_get_key(p_track, p_key_idx, &value);
+ return value;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1157,10 +1807,53 @@ 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);
+ if (tt->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<3>(tt->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
+ 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);
+ if (rt->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<3>(rt->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
+ 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);
+ if (st->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<3>(st->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
+ 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);
+ if (bst->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<1>(bst->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
+ 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 +1895,44 @@ 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_COND(tt->compressed_track >= 0);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ TKey<Vector3> key = tt->positions[p_key_idx];
key.time = p_time;
- tt->transforms.remove(p_key_idx);
- _insert(p_time, tt->transforms, key);
+ 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_COND(tt->compressed_track >= 0);
+ 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_COND(tt->compressed_track >= 0);
+ ERR_FAIL_INDEX(p_key_idx, tt->scales.size());
+ TKey<Vector3> key = tt->scales[p_key_idx];
+ key.time = p_time;
+ 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_COND(tt->compressed_track >= 0);
+ 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 +1990,37 @@ 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);
+ if (tt->compressed_track >= 0) {
+ return 1.0;
+ }
+ 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);
+ if (rt->compressed_track >= 0) {
+ return 1.0;
+ }
+ 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);
+ if (st->compressed_track >= 0) {
+ return 1.0;
+ }
+ 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);
+ if (bst->compressed_track >= 0) {
+ return 1.0;
+ }
+ 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);
@@ -1297,26 +2048,74 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_V(0);
}
+bool Animation::track_is_compressed(int p_track) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), false);
+ Track *t = tracks[p_track];
+
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ return tt->compressed_track >= 0;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ return rt->compressed_track >= 0;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ return st->compressed_track >= 0;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ return bst->compressed_track >= 0;
+ } break;
+ default: {
+ return false; //animation does not really use transitions
+ } break;
+ }
+
+ ERR_FAIL_V(false);
+}
+
void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p_value) {
ERR_FAIL_INDEX(p_track, tracks.size());
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_COND(tt->compressed_track >= 0);
+ 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_COND(rt->compressed_track >= 0);
+ 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_COND(st->compressed_track >= 0);
+ 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_COND(bst->compressed_track >= 0);
+ 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 +2184,29 @@ 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_COND(tt->compressed_track >= 0);
+ 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_COND(rt->compressed_track >= 0);
+ 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_COND(st->compressed_track >= 0);
+ 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_COND(bst->compressed_track >= 0);
+ 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);
@@ -1413,7 +2231,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
}
template <class K>
-int Animation::_find(const Vector<K> &p_keys, double p_time) const {
+int Animation::_find(const Vector<K> &p_keys, double p_time, bool p_backward) const {
int len = p_keys.size();
if (len == 0) {
return -2;
@@ -1443,22 +2261,19 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
}
}
- if (keys[middle].time > p_time) {
- middle--;
+ if (!p_backward) {
+ if (keys[middle].time > p_time) {
+ middle--;
+ }
+ } else {
+ if (keys[middle].time < p_time) {
+ middle++;
+ }
}
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 +2292,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);
}
@@ -1520,10 +2325,11 @@ Variant Animation::_cubic_interpolate(const Variant &p_pre_a, const Variant &p_a
real_t t2 = t * t;
real_t t3 = t2 * t;
- return 0.5f * ((p1 * 2.0f) +
- (-p0 + p2) * t +
- (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
- (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
+ return 0.5f *
+ ((p1 * 2.0f) +
+ (-p0 + p2) * t +
+ (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
+ (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
} else if ((vformat & (vformat - 1))) {
return p_a; //can't interpolate, mix of types
@@ -1585,7 +2391,7 @@ real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, c
}
template <class T>
-T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
+T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward) const {
int len = _find(p_keys, length) + 1; // try to find last key (there may be more past the end)
if (len <= 0) {
@@ -1603,7 +2409,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
return p_keys[0].value;
}
- int idx = _find(p_keys, p_time);
+ int idx = _find(p_keys, p_time, p_backward);
ERR_FAIL_COND_V(idx == -2, T());
@@ -1612,24 +2418,42 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t c = 0.0;
// prepare for all cases of interpolation
- if (loop && p_loop_wrap) {
+ if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) {
// loop
- if (idx >= 0) {
- if ((idx + 1) < len) {
- next = idx + 1;
- real_t delta = p_keys[next].time - p_keys[idx].time;
- real_t from = p_time - p_keys[idx].time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ if (!p_backward) {
+ // no backward
+ if (idx >= 0) {
+ if (idx < len - 1) {
+ next = idx + 1;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ next = 0;
+ real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
}
-
} else {
+ // on loop, behind first key
+ idx = len - 1;
next = 0;
- real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
- real_t from = p_time - p_keys[idx].time;
+ real_t endtime = (length - p_keys[idx].time);
+ if (endtime < 0) { // may be keys past the end
+ endtime = 0;
+ }
+ real_t delta = endtime + p_keys[next].time;
+ real_t from = endtime + p_time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -1637,49 +2461,81 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
c = from / delta;
}
}
-
} else {
- // on loop, behind first key
- idx = len - 1;
- next = 0;
- real_t endtime = (length - p_keys[idx].time);
- if (endtime < 0) { // may be keys past the end
- endtime = 0;
- }
- real_t delta = endtime + p_keys[next].time;
- real_t from = endtime + p_time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ // backward
+ if (idx <= len - 1) {
+ if (idx > 0) {
+ next = idx - 1;
+ real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
+ } else {
+ next = len - 1;
+ real_t delta = p_keys[idx].time + (length - p_keys[next].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ // on loop, in front of last key
+ idx = 0;
+ next = len - 1;
+ real_t endtime = p_keys[idx].time;
+ if (endtime > length) // may be keys past the end
+ endtime = length;
+ real_t delta = p_keys[next].time - endtime;
+ real_t from = p_time - endtime;
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
}
}
-
} else { // no loop
-
- if (idx >= 0) {
- if ((idx + 1) < len) {
- next = idx + 1;
- real_t delta = p_keys[next].time - p_keys[idx].time;
- real_t from = p_time - p_keys[idx].time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ if (!p_backward) {
+ if (idx >= 0) {
+ if (idx < len - 1) {
+ next = idx + 1;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ next = idx;
}
-
} else {
- next = idx;
+ idx = next = 0;
}
-
} else {
- // only allow extending first key to anim start if looping
- if (loop) {
- idx = next = 0;
+ if (idx <= len - 1) {
+ if (idx > 0) {
+ next = idx - 1;
+ real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
+
+ } else {
+ next = idx;
+ }
} else {
- result = false;
+ idx = next = len - 1;
}
}
}
@@ -1729,36 +2585,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];
@@ -1813,7 +2639,7 @@ void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, doub
}
}
-void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
@@ -1827,30 +2653,50 @@ void Animation::value_track_get_key_indices(int p_track, double p_time, double p
SWAP(from_time, to_time);
}
- if (loop) {
- from_time = Math::fposmod(from_time, length);
- to_time = Math::fposmod(to_time, length);
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- if (from_time > to_time) {
- // handle loop by splitting
- _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
- _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ from_time = Math::fposmod(from_time, length);
+ to_time = Math::fposmod(to_time, length);
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if (from_time > to_time) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
+ _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ from_time = Math::pingpong(from_time, length);
+ to_time = Math::pingpong(to_time, length);
+
+ if (p_pingponged == -1) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, 0, from_time, p_indices);
+ _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
+ _value_track_get_key_indices_in_range(vt, to_time, length, p_indices);
+ return;
+ }
+ } break;
}
_value_track_get_key_indices_in_range(vt, from_time, to_time, p_indices);
@@ -1909,7 +2755,7 @@ void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double
}
}
-void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
const Track *t = tracks[p_track];
@@ -1920,104 +2766,309 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
SWAP(from_time, to_time);
}
- if (loop) {
- if (from_time > length || from_time < 0) {
- from_time = Math::fposmod(from_time, length);
- }
-
- if (to_time > length || to_time < 0) {
- to_time = Math::fposmod(to_time, length);
- }
-
- if (from_time > to_time) {
- // 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);
-
- } break;
- case TYPE_VALUE: {
- const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_METHOD: {
- const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
- _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
-
- } break;
- case TYPE_BEZIER: {
- const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_AUDIO: {
- const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- } break;
- case TYPE_ANIMATION: {
- const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::fposmod(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::fposmod(to_time, length);
+ }
- } break;
+ if (from_time > to_time) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _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);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _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);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _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);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _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: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ } break;
+ }
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::pingpong(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::pingpong(to_time, length);
}
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) {
+ if (p_pingponged == -1) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, 0, from_time, 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);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, 0, from_time, 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);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, 0, from_time, 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);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ } break;
+ }
+ return;
+ }
+ if (p_pingponged == 1) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, to_time, length, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, to_time, length, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, to_time, length, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, to_time, length, p_indices);
+ } break;
+ }
+ return;
+ }
+ }
+ } break;
}
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);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices);
+ } else {
+ _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);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices);
+ } else {
+ _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);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices);
+ } else {
+ _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);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
+ }
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
-
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices);
-
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices);
-
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices);
-
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, to_time, p_indices);
-
} break;
}
}
@@ -2055,7 +3106,7 @@ void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, do
}
}
-void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_METHOD);
@@ -2069,35 +3120,58 @@ void Animation::method_track_get_key_indices(int p_track, double p_time, double
SWAP(from_time, to_time);
}
- if (loop) {
- if (from_time > length || from_time < 0) {
- from_time = Math::fposmod(from_time, length);
- }
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- if (to_time > length || to_time < 0) {
- to_time = Math::fposmod(to_time, length);
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::fposmod(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::fposmod(to_time, length);
+ }
- if (from_time > to_time) {
- // handle loop by splitting
- _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
- _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
+ if (from_time > to_time) {
+ // handle loop by splitting
+ _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
+ _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::pingpong(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::pingpong(to_time, length);
+ }
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if (p_pingponged == -1) {
+ _method_track_get_key_indices_in_range(mt, 0, from_time, p_indices);
+ _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
+ _method_track_get_key_indices_in_range(mt, to_time, length, p_indices);
+ return;
+ }
+ } break;
+ default:
+ break;
}
_method_track_get_key_indices_in_range(mt, from_time, to_time, p_indices);
@@ -2483,13 +3557,13 @@ real_t Animation::get_length() const {
return length;
}
-void Animation::set_loop(bool p_enabled) {
- loop = p_enabled;
+void Animation::set_loop_mode(Animation::LoopMode p_loop_mode) {
+ loop_mode = p_loop_mode;
emit_changed();
}
-bool Animation::has_loop() const {
- return loop;
+Animation::LoopMode Animation::get_loop_mode() const {
+ return loop_mode;
}
void Animation::track_set_imported(int p_track, bool p_imported) {
@@ -2595,7 +3669,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type);
ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path);
ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path);
- ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track);
+ ClassDB::bind_method(D_METHOD("find_track", "path", "type"), &Animation::find_track);
ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up);
ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down);
@@ -2608,7 +3682,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 +3706,8 @@ 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("track_is_compressed", "track_idx"), &Animation::track_is_compressed);
+
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);
@@ -2666,8 +3745,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
- ClassDB::bind_method(D_METHOD("set_loop", "enabled"), &Animation::set_loop);
- ClassDB::bind_method(D_METHOD("has_loop"), &Animation::has_loop);
+ ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &Animation::set_loop_mode);
+ ClassDB::bind_method(D_METHOD("get_loop_mode"), &Animation::get_loop_mode);
ClassDB::bind_method(D_METHOD("set_step", "size_sec"), &Animation::set_step);
ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step);
@@ -2675,14 +3754,19 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
+ ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step");
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);
@@ -2696,6 +3780,10 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_DISCRETE);
BIND_ENUM_CONSTANT(UPDATE_TRIGGER);
BIND_ENUM_CONSTANT(UPDATE_CAPTURE);
+
+ BIND_ENUM_CONSTANT(LOOP_NONE);
+ BIND_ENUM_CONSTANT(LOOP_LINEAR);
+ BIND_ENUM_CONSTANT(LOOP_PINGPONG);
}
void Animation::clear() {
@@ -2703,227 +3791,1479 @@ void Animation::clear() {
memdelete(tracks[i]);
}
tracks.clear();
- loop = false;
+ loop_mode = LOOP_NONE;
length = 1;
+ compression.enabled = false;
+ compression.bounds.clear();
+ compression.pages.clear();
+ compression.fps = 120;
emit_changed();
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;
+
+ 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;
+ }
+
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+
+ if (d > pd.length() * p_allowed_linear_err) {
+ return false; //beyond allowed error for collinearity
+ }
+
+ if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+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;
- { //translation
+ //localize both to rotation from q0
- const Vector3 &v0 = t0.value.loc;
- const Vector3 &v1 = t1.value.loc;
- const Vector3 &v2 = t2.value.loc;
+ if (q0.is_equal_approx(q2)) {
+ if (!q0.is_equal_approx(q1)) {
+ 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;
+ } else {
+ Quaternion r02 = (q0.inverse() * q2).normalized();
+ Quaternion r01 = (q0.inverse() * q1).normalized();
+
+ Vector3 v02, v01;
+ real_t a02, a01;
+
+ r02.get_axis_angle(v02, a02);
+ r01.get_axis_angle(v01, a01);
+
+ if (Math::abs(a02) > p_max_optimizable_angle) {
+ return false;
+ }
+
+ if (v01.dot(v02) < 0) {
+ //make sure both rotations go the same way to compare
+ v02 = -v02;
+ a02 = -a02;
+ }
+
+ 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;
+ }
+
+ if (a01 * a02 < 0) {
+ //not rotating in the same direction
+ return false;
+ }
+
+ real_t tr = a01 / a02;
+ if (tr < 0 || tr > 1) {
+ return false; //rotating too much or too less
+ }
+ }
+
+ return true;
+}
+
+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 (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
+ }
+
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+
+ if (d > pd.length() * p_allowed_linear_error) {
+ return false; //beyond allowed error for colinearity
+ }
+ }
+
+ return true;
+}
+
+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 (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;
+ }
+
+ } 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
+ }
+
+ float s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+
+ if (d > pd.length() * p_allowed_linear_error) {
+ return false; //beyond allowed error for colinearity
+ }
+*/
+ }
+
+ return true;
+}
+
+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();
+ }
+
+ 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 (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
+ tt->positions.remove(i);
+ i--;
+
} 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;
- }
+ 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];
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle);
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
+ 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;
}
- if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) {
- return false;
+ tt->rotations.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
+ }
+ }
+}
+
+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_SCALE_3D);
+ ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Vector3> first_erased;
+
+ 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];
+
+ bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
+
+ 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 (erase) {
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
- t[0] = (d1 - d0) / (d2 - d0);
+ tt->scales.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
}
}
+}
- { //rotation
+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;
- const Quaternion &q0 = t0.value.rot;
- const Quaternion &q1 = t1.value.rot;
- const Quaternion &q2 = t2.value.rot;
+ 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];
- //localize both to rotation from q0
+ bool erase = _blend_shape_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
- if (q0.is_equal_approx(q2)) {
- if (!q0.is_equal_approx(q1)) {
- return false;
+ 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;
+ }
+
+ if (erase) {
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
+ tt->blend_shapes.remove(i);
+ i--;
+
} else {
- Quaternion r02 = (q0.inverse() * q2).normalized();
- Quaternion r01 = (q0.inverse() * q1).normalized();
+ prev_erased = false;
+ }
+ }
+}
+
+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 (track_is_compressed(i)) {
+ continue; //not possible to optimize compressed track
+ }
+ 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);
+ }
+ }
+}
- Vector3 v02, v01;
- real_t a02, a01;
+#define print_animc(m_str)
+//#define print_animc(m_str) print_line(m_str);
- r02.get_axis_angle(v02, a02);
- r01.get_axis_angle(v01, a01);
+struct AnimationCompressionDataState {
+ enum {
+ MIN_OPTIMIZE_PACKETS = 5,
+ MAX_PACKETS = 16
+ };
- if (Math::abs(a02) > p_max_optimizable_angle) {
- return false;
+ uint32_t components = 3;
+ LocalVector<uint8_t> data; //commited packets
+ struct PacketData {
+ int32_t data[3] = { 0, 0, 0 };
+ uint32_t frame = 0;
+ };
+
+ float split_tolerance = 1.5;
+
+ LocalVector<PacketData> temp_packets;
+
+ //used for rollback if the new frame does not fit
+ int32_t validated_packet_count = -1;
+
+ static int32_t _compute_delta16_signed(int32_t p_from, int32_t p_to) {
+ int32_t delta = p_to - p_from;
+ if (delta > 32767) {
+ return delta - 65536; // use wrap around
+ } else if (delta < -32768) {
+ return 65536 + delta; // use wrap around
+ }
+ return delta;
+ }
+
+ static uint32_t _compute_shift_bits_signed(int32_t p_delta) {
+ if (p_delta == 0) {
+ return 0;
+ } else if (p_delta < 0) {
+ p_delta = ABS(p_delta) - 1;
+ if (p_delta == 0) {
+ return 1;
}
+ }
+ return nearest_shift(p_delta);
+ }
- if (v01.dot(v02) < 0) {
- //make sure both rotations go the same way to compare
- v02 = -v02;
- a02 = -a02;
+ void _compute_max_shifts(uint32_t p_from, uint32_t p_to, uint32_t *max_shifts, uint32_t &max_frame_delta_shift) const {
+ for (uint32_t j = 0; j < components; j++) {
+ max_shifts[j] = 0;
+ }
+ max_frame_delta_shift = 0;
+
+ for (uint32_t i = p_from + 1; i <= p_to; i++) {
+ int32_t frame_delta = temp_packets[i].frame - temp_packets[i - 1].frame;
+ max_frame_delta_shift = MAX(max_frame_delta_shift, nearest_shift(frame_delta));
+ for (uint32_t j = 0; j < components; j++) {
+ int32_t diff = _compute_delta16_signed(temp_packets[i - 1].data[j], temp_packets[i].data[j]);
+ uint32_t shift = _compute_shift_bits_signed(diff);
+ max_shifts[j] = MAX(shift, max_shifts[j]);
}
+ }
+ }
- 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 insert_key(uint32_t p_frame, const Vector3i &p_key) {
+ if (temp_packets.size() == MAX_PACKETS) {
+ commit_temp_packets();
+ }
+ PacketData packet;
+ packet.frame = p_frame;
+ for (int i = 0; i < 3; i++) {
+ ERR_FAIL_COND_V(p_key[i] > 65535, false); // Sanity check
+ packet.data[i] = p_key[i];
+ }
+
+ temp_packets.push_back(packet);
+
+ if (temp_packets.size() >= MIN_OPTIMIZE_PACKETS) {
+ uint32_t max_shifts[3] = { 0, 0, 0 }; // Base sizes, 16 bit
+ uint32_t max_frame_delta_shift = 0;
+ // Compute the average shift before the packet was added
+ _compute_max_shifts(0, temp_packets.size() - 2, max_shifts, max_frame_delta_shift);
+
+ float prev_packet_size_avg = 0;
+ prev_packet_size_avg = float(1 << max_frame_delta_shift);
+ for (uint32_t i = 0; i < components; i++) {
+ prev_packet_size_avg += float(1 << max_shifts[i]);
}
+ prev_packet_size_avg /= float(1 + components);
- if (a01 * a02 < 0) {
- //not rotating in the same direction
- return false;
+ _compute_max_shifts(temp_packets.size() - 2, temp_packets.size() - 1, max_shifts, max_frame_delta_shift);
+
+ float new_packet_size_avg = 0;
+ new_packet_size_avg = float(1 << max_frame_delta_shift);
+ for (uint32_t i = 0; i < components; i++) {
+ new_packet_size_avg += float(1 << max_shifts[i]);
}
+ new_packet_size_avg /= float(1 + components);
+
+ print_animc("packet count: " + rtos(temp_packets.size() - 1) + " size avg " + rtos(prev_packet_size_avg) + " new avg " + rtos(new_packet_size_avg));
+ float ratio = (prev_packet_size_avg < new_packet_size_avg) ? (new_packet_size_avg / prev_packet_size_avg) : (prev_packet_size_avg / new_packet_size_avg);
- real_t tr = a01 / a02;
- if (tr < 0 || tr > 1) {
- return false; //rotating too much or too less
+ if (ratio > split_tolerance) {
+ print_animc("split!");
+ temp_packets.resize(temp_packets.size() - 1);
+ commit_temp_packets();
+ temp_packets.push_back(packet);
}
+ }
+
+ return temp_packets.size() == 1; // First key
+ }
+
+ uint32_t get_temp_packet_size() const {
+ if (temp_packets.size() == 0) {
+ return 0;
+ } else if (temp_packets.size() == 1) {
+ return components == 1 ? 4 : 8; // 1 component packet is 16 bits and 16 bits unused. 3 component packets is 48 bits and 16 bits unused
+ }
+ uint32_t max_shifts[3] = { 0, 0, 0 }; //base sizes, 16 bit
+ uint32_t max_frame_delta_shift = 0;
+
+ _compute_max_shifts(0, temp_packets.size() - 1, max_shifts, max_frame_delta_shift);
+
+ uint32_t size_bits = 16; //base value (all 4 bits of shift sizes for x,y,z,time)
+ size_bits += max_frame_delta_shift * (temp_packets.size() - 1); //times
+ for (uint32_t j = 0; j < components; j++) {
+ size_bits += 16; //base value
+ uint32_t shift = max_shifts[j];
+ if (shift > 0) {
+ shift += 1; //if not zero, add sign bit
+ }
+ size_bits += shift * (temp_packets.size() - 1);
+ }
+ if (size_bits % 8 != 0) { //wrap to 8 bits
+ size_bits += 8 - (size_bits % 8);
+ }
+ uint32_t size_bytes = size_bits / 8; //wrap to words
+ if (size_bytes % 4 != 0) {
+ size_bytes += 4 - (size_bytes % 4);
+ }
+ return size_bytes;
+ }
- t[1] = tr;
+ static void _push_bits(LocalVector<uint8_t> &data, uint32_t &r_buffer, uint32_t &r_bits_used, uint32_t p_value, uint32_t p_bits) {
+ r_buffer |= p_value << r_bits_used;
+ r_bits_used += p_bits;
+ while (r_bits_used >= 8) {
+ uint8_t byte = r_buffer & 0xFF;
+ data.push_back(byte);
+ r_buffer >>= 8;
+ r_bits_used -= 8;
}
}
- { //scale
+ void commit_temp_packets() {
+ if (temp_packets.size() == 0) {
+ return; //nohing to do
+ }
+#define DEBUG_PACKET_PUSH
+#ifdef DEBUG_PACKET_PUSH
+#ifndef _MSC_VER
+#warning Debugging packet push, disable this code in production to gain a bit more import performance.
+#endif
+ uint32_t debug_packet_push = get_temp_packet_size();
+ uint32_t debug_data_size = data.size();
+#endif
+ // Store header
+
+ uint8_t header[8];
+ uint32_t header_bytes = 0;
+ for (uint32_t i = 0; i < components; i++) {
+ encode_uint16(temp_packets[0].data[i], &header[header_bytes]);
+ header_bytes += 2;
+ }
- const Vector3 &v0 = t0.value.scale;
- const Vector3 &v1 = t1.value.scale;
- const Vector3 &v2 = t2.value.scale;
+ uint32_t max_shifts[3] = { 0, 0, 0 }; //base sizes, 16 bit
+ uint32_t max_frame_delta_shift = 0;
- 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;
+ if (temp_packets.size() > 1) {
+ _compute_max_shifts(0, temp_packets.size() - 1, max_shifts, max_frame_delta_shift);
+ uint16_t shift_header = (max_frame_delta_shift - 1) << 12;
+ for (uint32_t i = 0; i < components; i++) {
+ shift_header |= max_shifts[i] << (4 * i);
}
- } 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
+ encode_uint16(shift_header, &header[header_bytes]);
+ header_bytes += 2;
+ }
+
+ while (header_bytes % 4 != 0) {
+ header[header_bytes++] = 0;
+ }
+
+ for (uint32_t i = 0; i < header_bytes; i++) {
+ data.push_back(header[i]);
+ }
+
+ if (temp_packets.size() == 1) {
+ temp_packets.clear();
+ validated_packet_count = 0;
+ return; //only header stored, nothing else to do
+ }
+
+ uint32_t bit_buffer = 0;
+ uint32_t bits_used = 0;
+
+ for (uint32_t i = 1; i < temp_packets.size(); i++) {
+ uint32_t frame_delta = temp_packets[i].frame - temp_packets[i - 1].frame;
+ _push_bits(data, bit_buffer, bits_used, frame_delta, max_frame_delta_shift);
+
+ for (uint32_t j = 0; j < components; j++) {
+ if (max_shifts[j] == 0) {
+ continue; // Zero delta, do not store
+ }
+ int32_t delta = _compute_delta16_signed(temp_packets[i - 1].data[j], temp_packets[i].data[j]);
+
+ ERR_FAIL_COND(delta < -32768 || delta > 32767); //sanity check
+
+ uint16_t deltau;
+ if (delta < 0) {
+ deltau = (ABS(delta) - 1) | (1 << max_shifts[j]);
+ } else {
+ deltau = delta;
+ }
+ _push_bits(data, bit_buffer, bits_used, deltau, max_shifts[j] + 1); // Include sign bit
}
+ }
+ if (bits_used != 0) {
+ ERR_FAIL_COND(bit_buffer > 0xFF); // Sanity check
+ data.push_back(bit_buffer);
+ }
+
+ while (data.size() % 4 != 0) {
+ data.push_back(0); //pad to align with 4
+ }
+
+ temp_packets.clear();
+ validated_packet_count = 0;
+
+#ifdef DEBUG_PACKET_PUSH
+ ERR_FAIL_COND((data.size() - debug_data_size) != debug_packet_push);
+#endif
+ }
+};
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+struct AnimationCompressionTimeState {
+ struct Packet {
+ uint32_t frame;
+ uint32_t offset;
+ uint32_t count;
+ };
+
+ LocalVector<Packet> packets;
+ //used for rollback
+ int32_t key_index = 0;
+ int32_t validated_packet_count = 0;
+ int32_t validated_key_index = -1;
+ bool needs_start_frame = false;
+};
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
+Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_t p_key, float p_time) {
+ Vector3i values;
+ TrackType tt = track_get_type(p_track);
+ switch (tt) {
+ case TYPE_POSITION_3D: {
+ Vector3 pos;
+ if (p_key >= 0) {
+ position_track_get_key(p_track, p_key, &pos);
+ } else {
+ position_track_interpolate(p_track, p_time, &pos);
}
+ pos = (pos - p_bounds.position) / p_bounds.size;
+ for (int j = 0; j < 3; j++) {
+ values[j] = CLAMP(int32_t(pos[j] * 65535.0), 0, 65535);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ Quaternion rot;
+ if (p_key >= 0) {
+ rotation_track_get_key(p_track, p_key, &rot);
+ } else {
+ rotation_track_interpolate(p_track, p_time, &rot);
+ }
+ Vector3 axis = rot.get_axis();
+ float angle = rot.get_angle();
+ angle = Math::fposmod(double(angle), double(Math_PI * 2.0));
+ Vector2 oct = axis.octahedron_encode();
+ Vector3 rot_norm(oct.x, oct.y, angle / (Math_PI * 2.0)); // high resolution rotation in 0-1 angle.
+
+ for (int j = 0; j < 3; j++) {
+ values[j] = CLAMP(int32_t(rot_norm[j] * 65535.0), 0, 65535);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ Vector3 scale;
+ if (p_key >= 0) {
+ scale_track_get_key(p_track, p_key, &scale);
+ } else {
+ scale_track_interpolate(p_track, p_time, &scale);
+ }
+ scale = (scale - p_bounds.position) / p_bounds.size;
+ for (int j = 0; j < 3; j++) {
+ values[j] = CLAMP(int32_t(scale[j] * 65535.0), 0, 65535);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ float blend;
+ if (p_key >= 0) {
+ blend_shape_track_get_key(p_track, p_key, &blend);
+ } else {
+ blend_shape_track_interpolate(p_track, p_time, &blend);
+ }
+
+ blend = (blend / float(Compression::BLEND_SHAPE_RANGE)) * 0.5 + 0.5;
+ values[0] = CLAMP(int32_t(blend * 65535.0), 0, 65535);
+ } break;
+ default: {
+ ERR_FAIL_V(Vector3i()); //sanity check
+ } break;
+ }
+
+ return values;
+}
- t[2] = (d1 - d0) / (d2 - d0);
+struct AnimationCompressionBufferBitsRead {
+ uint32_t buffer = 0;
+ uint32_t used = 0;
+ const uint8_t *src_data = nullptr;
+
+ _FORCE_INLINE_ uint32_t read(uint32_t p_bits) {
+ uint32_t output = 0;
+ uint32_t written = 0;
+ while (p_bits > 0) {
+ if (used == 0) {
+ used = 8;
+ buffer = *src_data;
+ src_data++;
+ }
+ uint32_t to_write = MIN(used, p_bits);
+ output |= (buffer & ((1 << to_write) - 1)) << written;
+ buffer >>= to_write;
+ used -= to_write;
+ p_bits -= to_write;
+ written += to_write;
}
+ return output;
}
+};
- 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;
+void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tolerance) {
+ ERR_FAIL_COND_MSG(compression.enabled, "This animation is already compressed");
+
+ p_split_tolerance = CLAMP(p_split_tolerance, 1.1, 8.0);
+ compression.pages.clear();
+
+ uint32_t base_page_size = 0; // Before compressing pages, compute how large the "end page" datablock is.
+ LocalVector<uint32_t> tracks_to_compress;
+ LocalVector<AABB> track_bounds;
+ const uint32_t time_packet_size = 4;
+
+ const uint32_t track_header_size = 4 + 4 + 4; // pointer to time (4 bytes), amount of time keys (4 bytes) pointer to track data (4 bytes)
+
+ for (int i = 0; i < get_track_count(); i++) {
+ TrackType type = track_get_type(i);
+ if (type != TYPE_POSITION_3D && type != TYPE_ROTATION_3D && type != TYPE_SCALE_3D && type != TYPE_BLEND_SHAPE) {
+ continue;
+ }
+ if (track_get_key_count(i) == 0) {
+ continue; //do not compress, no keys
+ }
+ base_page_size += track_header_size; //pointer to beginning of each track timeline and amount of time keys
+ base_page_size += time_packet_size; //for end of track time marker
+ base_page_size += (type == TYPE_BLEND_SHAPE) ? 4 : 8; // at least the end of track packet (at much 8 bytes). This could be less, but have to be pessimistic.
+ tracks_to_compress.push_back(i);
+
+ AABB bounds;
+
+ if (type == TYPE_POSITION_3D) {
+ AABB aabb;
+ int kcount = track_get_key_count(i);
+ for (int j = 0; j < kcount; j++) {
+ Vector3 pos;
+ position_track_get_key(i, j, &pos);
+ if (j == 0) {
+ aabb.position = pos;
+ } else {
+ aabb.expand_to(pos);
+ }
+ }
+ for (int j = 0; j < 3; j++) {
+ //cant have zero
+ if (aabb.size[j] < CMP_EPSILON) {
+ aabb.size[j] = CMP_EPSILON;
+ }
+ }
+ bounds = aabb;
+ }
+ if (type == TYPE_SCALE_3D) {
+ AABB aabb;
+ int kcount = track_get_key_count(i);
+ for (int j = 0; j < kcount; j++) {
+ Vector3 scale;
+ scale_track_get_key(i, j, &scale);
+ if (j == 0) {
+ aabb.position = scale;
+ } else {
+ aabb.expand_to(scale);
+ }
+ }
+ for (int j = 0; j < 3; j++) {
+ //cant have zero
+ if (aabb.size[j] < CMP_EPSILON) {
+ aabb.size[j] = CMP_EPSILON;
+ }
+ }
+ bounds = aabb;
+ }
+
+ track_bounds.push_back(bounds);
+ }
+
+ if (tracks_to_compress.size() == 0) {
+ return; //nothing to compress
+ }
+
+ print_animc("Anim Compression:");
+ print_animc("-----------------");
+ print_animc("Tracks to compress: " + itos(tracks_to_compress.size()));
+
+ uint32_t current_frame = 0;
+ uint32_t base_page_frame = 0;
+ double frame_len = 1.0 / double(p_fps);
+ const uint32_t max_frames_per_page = 65536;
+
+ print_animc("Frame Len: " + rtos(frame_len));
+
+ LocalVector<AnimationCompressionDataState> data_tracks;
+ LocalVector<AnimationCompressionTimeState> time_tracks;
+
+ data_tracks.resize(tracks_to_compress.size());
+ time_tracks.resize(tracks_to_compress.size());
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ data_tracks[i].split_tolerance = p_split_tolerance;
+ if (track_get_type(tracks_to_compress[i]) == TYPE_BLEND_SHAPE) {
+ data_tracks[i].components = 1;
+ } else {
+ data_tracks[i].components = 3;
+ }
+ }
+
+ while (true) {
+ // Begin by finding the keyframe in all tracks with the time closest to the current time
+ const uint32_t FRAME_MAX = 0xFFFFFFFF;
+ const int32_t NO_TRACK_FOUND = -1;
+ uint32_t best_frame = FRAME_MAX;
+ uint32_t best_invalid_frame = FRAME_MAX;
+ int32_t best_frame_track = NO_TRACK_FOUND; // Default is -1, which means all keyframes for this page are exhausted.
+ bool start_frame = false;
+
+ for (uint32_t i = 0; i < tracks_to_compress.size(); i++) {
+ uint32_t uncomp_track = tracks_to_compress[i];
+
+ if (time_tracks[i].key_index == track_get_key_count(uncomp_track)) {
+ if (time_tracks[i].needs_start_frame) {
+ start_frame = true;
+ best_frame = base_page_frame;
+ best_frame_track = i;
+ time_tracks[i].needs_start_frame = false;
+ break;
+ } else {
+ continue; // This track is exhausted (all keys were added already), don't consider.
+ }
+ }
+
+ uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len;
+
+ if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) {
+ start_frame = true;
+ best_frame = base_page_frame;
+ best_frame_track = i;
+ time_tracks[i].needs_start_frame = false;
+ break;
+ }
+
+ ERR_FAIL_COND(key_frame < base_page_frame); // Sanity check, should never happen
+
+ if (key_frame - base_page_frame >= max_frames_per_page) {
+ // Invalid because beyond the max frames allowed per page
+ best_invalid_frame = MIN(best_invalid_frame, key_frame);
+ } else if (key_frame < best_frame) {
+ best_frame = key_frame;
+ best_frame_track = i;
+ }
+ }
+
+ print_animc("*KEY*: Current Frame: " + itos(current_frame) + " Best Frame: " + rtos(best_frame) + " Best Track: " + itos(best_frame_track) + " Start: " + String(start_frame ? "true" : "false"));
+
+ if (!start_frame && best_frame > current_frame) {
+ // Any case where the current frame advanced, either because nothing was found or because something was found greater than the current one.
+ print_animc("\tAdvance Condition.");
+ bool rollback = false;
+
+ // The frame has advanced, time to validate the previous frame
+ uint32_t current_page_size = base_page_size;
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ uint32_t track_size = data_tracks[i].data.size(); // track size
+ track_size += data_tracks[i].get_temp_packet_size(); // Add the temporary data
+ if (track_size > Compression::MAX_DATA_TRACK_SIZE) {
+ rollback = true; //track to large, time track can't point to keys any longer, because key offset is 12 bits
+ break;
+ }
+ current_page_size += track_size;
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ current_page_size += time_tracks[i].packets.size() * 4; // time packet is 32 bits
+ }
+
+ if (!rollback && current_page_size > p_page_size) {
+ rollback = true;
+ }
+
+ print_animc("\tCurrent Page Size: " + itos(current_page_size) + "/" + itos(p_page_size) + " Rollback? " + String(rollback ? "YES!" : "no"));
+
+ if (rollback) {
+ // Not valid any longer, so rollback and commit page
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ data_tracks[i].temp_packets.resize(data_tracks[i].validated_packet_count);
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ time_tracks[i].key_index = time_tracks[i].validated_key_index; //rollback key
+ time_tracks[i].packets.resize(time_tracks[i].validated_packet_count);
+ }
+
+ } else {
+ // All valid, so save rollback information
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ data_tracks[i].validated_packet_count = data_tracks[i].temp_packets.size();
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ time_tracks[i].validated_key_index = time_tracks[i].key_index;
+ time_tracks[i].validated_packet_count = time_tracks[i].packets.size();
+ }
+
+ // Accept this frame as the frame being processed (as long as it exists)
+ if (best_frame != FRAME_MAX) {
+ current_frame = best_frame;
+ print_animc("\tValidated, New Current Frame: " + itos(current_frame));
+ }
+ }
+
+ if (rollback || best_frame == FRAME_MAX) {
+ // Commit the page if had to rollback or if no track was found
+ print_animc("\tCommiting page..");
+
+ // The end frame for the page depends entirely on whether its valid or
+ // no more keys were found.
+ // If not valid, then the end frame is the current frame (as this means the current frame is being rolled back
+ // If valid, then the end frame is the next invalid one (in case more frames exist), or the current frame in case no more frames exist.
+ uint32_t page_end_frame = (rollback || best_frame == FRAME_MAX) ? current_frame : best_invalid_frame;
+
+ print_animc("\tEnd Frame: " + itos(page_end_frame) + ", " + rtos(page_end_frame * frame_len) + "s");
+
+ // Add finalizer frames and commit pending tracks
+ uint32_t finalizer_local_frame = page_end_frame - base_page_frame;
+
+ uint32_t total_page_size = 0;
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ if (data_tracks[i].temp_packets.size() == 0 || (data_tracks[i].temp_packets[data_tracks[i].temp_packets.size() - 1].frame) < finalizer_local_frame) {
+ // Add finalizer frame if it makes sense
+ Vector3i values = _compress_key(tracks_to_compress[i], track_bounds[i], -1, page_end_frame * frame_len);
+
+ bool first_key = data_tracks[i].insert_key(finalizer_local_frame, values);
+ if (first_key) {
+ AnimationCompressionTimeState::Packet p;
+ p.count = 1;
+ p.frame = finalizer_local_frame;
+ p.offset = data_tracks[i].data.size();
+ time_tracks[i].packets.push_back(p);
+ } else {
+ ERR_FAIL_COND(time_tracks[i].packets.size() == 0);
+ time_tracks[i].packets[time_tracks[i].packets.size() - 1].count++;
+ }
}
- if (Math::abs(lt - t[k]) > p_alowed_linear_err) {
- erase = false;
- break;
+ data_tracks[i].commit_temp_packets();
+ total_page_size += data_tracks[i].data.size();
+ total_page_size += time_tracks[i].packets.size() * 4;
+ total_page_size += track_header_size;
+
+ print_animc("\tTrack " + itos(i) + " time packets: " + itos(time_tracks[i].packets.size()) + " Packet data: " + itos(data_tracks[i].data.size()));
+ }
+
+ print_animc("\tTotal page Size: " + itos(total_page_size) + "/" + itos(p_page_size));
+
+ // Create Page
+ Vector<uint8_t> page_data;
+ page_data.resize(total_page_size);
+ {
+ uint8_t *page_ptr = page_data.ptrw();
+ uint32_t base_offset = data_tracks.size() * track_header_size;
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ encode_uint32(base_offset, page_ptr + (track_header_size * i + 0));
+ uint16_t *key_time_ptr = (uint16_t *)(page_ptr + base_offset);
+ for (uint32_t j = 0; j < time_tracks[i].packets.size(); j++) {
+ key_time_ptr[j * 2 + 0] = uint16_t(time_tracks[i].packets[j].frame);
+ uint16_t ptr = time_tracks[i].packets[j].offset / 4;
+ ptr |= (time_tracks[i].packets[j].count - 1) << 12;
+ key_time_ptr[j * 2 + 1] = ptr;
+ base_offset += 4;
+ }
+ encode_uint32(time_tracks[i].packets.size(), page_ptr + (track_header_size * i + 4));
+ encode_uint32(base_offset, page_ptr + (track_header_size * i + 8));
+ memcpy(page_ptr + base_offset, data_tracks[i].data.ptr(), data_tracks[i].data.size());
+ base_offset += data_tracks[i].data.size();
+
+ //reset track
+ data_tracks[i].data.clear();
+ data_tracks[i].temp_packets.clear();
+ data_tracks[i].validated_packet_count = -1;
+
+ time_tracks[i].needs_start_frame = true; //Not required the first time, but from now on it is.
+ time_tracks[i].packets.clear();
+ time_tracks[i].validated_key_index = -1;
+ time_tracks[i].validated_packet_count = 0;
}
}
- break;
+
+ Compression::Page page;
+ page.data = page_data;
+ page.time_offset = base_page_frame * frame_len;
+ compression.pages.push_back(page);
+
+ if (!rollback && best_invalid_frame == FRAME_MAX) {
+ break; // No more pages to add.
+ }
+
+ current_frame = page_end_frame;
+ base_page_frame = page_end_frame;
+
+ continue; // Start over
}
}
- ERR_FAIL_COND_V(lt == -1, false);
+ // A key was found for the current frame and all is ok
- 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;
+ uint32_t comp_track = best_frame_track;
+ Vector3i values;
+
+ if (start_frame) {
+ // Interpolate
+ values = _compress_key(tracks_to_compress[comp_track], track_bounds[comp_track], -1, base_page_frame * frame_len);
+ } else {
+ uint32_t key = time_tracks[comp_track].key_index;
+ values = _compress_key(tracks_to_compress[comp_track], track_bounds[comp_track], key);
+ time_tracks[comp_track].key_index++; //goto next key (but could be rolled back if beyond page size).
+ }
+
+ bool first_key = data_tracks[comp_track].insert_key(best_frame - base_page_frame, values);
+ if (first_key) {
+ AnimationCompressionTimeState::Packet p;
+ p.count = 1;
+ p.frame = best_frame - base_page_frame;
+ p.offset = data_tracks[comp_track].data.size();
+ time_tracks[comp_track].packets.push_back(p);
+ } else {
+ ERR_CONTINUE(time_tracks[comp_track].packets.size() == 0);
+ time_tracks[comp_track].packets[time_tracks[comp_track].packets.size() - 1].count++;
+ }
+ }
+
+ compression.bounds = track_bounds;
+ compression.fps = p_fps;
+ compression.enabled = true;
+
+ for (uint32_t i = 0; i < tracks_to_compress.size(); i++) {
+ Track *t = tracks[tracks_to_compress[i]];
+ t->interpolation = INTERPOLATION_LINEAR; //only linear supported
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ tt->positions.clear();
+ tt->compressed_track = i;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ rt->rotations.clear();
+ rt->compressed_track = i;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ st->scales.clear();
+ st->compressed_track = i;
+ print_line("Scale Bounds " + itos(i) + ": " + track_bounds[i]);
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ bst->blend_shapes.clear();
+ bst->compressed_track = i;
+ } break;
+ default: {
}
}
}
+#if 1
+ uint32_t orig_size = 0;
+ for (int i = 0; i < get_track_count(); i++) {
+ switch (track_get_type(i)) {
+ case TYPE_SCALE_3D:
+ case TYPE_POSITION_3D: {
+ orig_size += sizeof(TKey<Vector3>) * track_get_key_count(i);
+ } break;
+ case TYPE_ROTATION_3D: {
+ orig_size += sizeof(TKey<Quaternion>) * track_get_key_count(i);
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ orig_size += sizeof(TKey<float>) * track_get_key_count(i);
+ } break;
+ default: {
+ }
+ }
+ }
+
+ uint32_t new_size = 0;
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ new_size += compression.pages[i].data.size();
+ }
- return erase;
+ print_line("Original size: " + itos(orig_size) + " - Compressed size: " + itos(new_size) + " " + String::num(float(new_size) / float(orig_size) * 100, 2) + "% pages: " + itos(compression.pages.size()));
+#endif
}
-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) {
- ERR_FAIL_INDEX(p_idx, tracks.size());
- ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D);
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]);
- bool prev_erased = false;
- TKey<TransformKey> first_erased;
+bool Animation::_rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const {
+ Vector3i current;
+ Vector3i next;
+ double time_current;
+ double time_next;
- Vector3 norm;
+ if (!_fetch_compressed<3>(p_compressed_track, p_time, current, time_current, next, time_next)) {
+ return false; //some sort of problem
+ }
- 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];
+ if (time_current >= p_time || time_current == time_next) {
+ r_ret = _uncompress_quaternion(current);
+ } else if (p_time >= time_next) {
+ r_ret = _uncompress_quaternion(next);
+ } else {
+ double c = (p_time - time_current) / (time_next - time_current);
+ Quaternion from = _uncompress_quaternion(current);
+ Quaternion to = _uncompress_quaternion(next);
+ r_ret = from.slerp(to, c);
+ }
- 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();
+ return true;
+}
+
+bool Animation::_pos_scale_interpolate_compressed(uint32_t p_compressed_track, double p_time, Vector3 &r_ret) const {
+ Vector3i current;
+ Vector3i next;
+ double time_current;
+ double time_next;
+
+ if (!_fetch_compressed<3>(p_compressed_track, p_time, current, time_current, next, time_next)) {
+ return false; //some sort of problem
+ }
+
+ if (time_current >= p_time || time_current == time_next) {
+ r_ret = _uncompress_pos_scale(p_compressed_track, current);
+ } else if (p_time >= time_next) {
+ r_ret = _uncompress_pos_scale(p_compressed_track, next);
+ } else {
+ double c = (p_time - time_current) / (time_next - time_current);
+ Vector3 from = _uncompress_pos_scale(p_compressed_track, current);
+ Vector3 to = _uncompress_pos_scale(p_compressed_track, next);
+ r_ret = from.lerp(to, c);
+ }
+
+ return true;
+}
+bool Animation::_blend_shape_interpolate_compressed(uint32_t p_compressed_track, double p_time, float &r_ret) const {
+ Vector3i current;
+ Vector3i next;
+ double time_current;
+ double time_next;
+
+ if (!_fetch_compressed<1>(p_compressed_track, p_time, current, time_current, next, time_next)) {
+ return false; //some sort of problem
+ }
+
+ if (time_current >= p_time || time_current == time_next) {
+ r_ret = _uncompress_blend_shape(current);
+ } else if (p_time >= time_next) {
+ r_ret = _uncompress_blend_shape(next);
+ } else {
+ float c = (p_time - time_current) / (time_next - time_current);
+ float from = _uncompress_blend_shape(current);
+ float to = _uncompress_blend_shape(next);
+ r_ret = Math::lerp(from, to, c);
+ }
+
+ return true;
+}
+
+template <uint32_t COMPONENTS>
+bool Animation::_fetch_compressed(uint32_t p_compressed_track, double p_time, Vector3i &r_current_value, double &r_current_time, Vector3i &r_next_value, double &r_next_time, uint32_t *key_index) const {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false);
+ p_time = CLAMP(p_time, 0, length);
+ if (key_index) {
+ *key_index = 0;
+ }
+
+ double frame_to_sec = 1.0 / double(compression.fps);
+
+ int32_t page_index = -1;
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ if (compression.pages[i].time_offset > p_time) {
+ break;
}
+ page_index = i;
+ }
- if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) {
- //avoid error to go beyond first erased key
- erase = false;
+ ERR_FAIL_COND_V(page_index == -1, false); //should not happen
+
+ double page_base_time = compression.pages[page_index].time_offset;
+ const uint8_t *page_data = compression.pages[page_index].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+
+ int32_t packet_idx = 0;
+ double packet_time = double(time_keys[0]) * frame_to_sec + page_base_time;
+ uint32_t base_frame = time_keys[0];
+
+ for (uint32_t i = 1; i < time_key_count; i++) {
+ uint32_t f = time_keys[i * 2 + 0];
+ double frame_time = double(f) * frame_to_sec + page_base_time;
+
+ if (frame_time > p_time) {
+ break;
}
- if (erase) {
- if (!prev_erased) {
- first_erased = t1;
- prev_erased = true;
+ if (key_index) {
+ (*key_index) += (time_keys[(i - 1) * 2 + 1] >> 12) + 1;
+ }
+
+ packet_idx = i;
+ packet_time = frame_time;
+ base_frame = f;
+ }
+
+ const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]];
+
+ uint16_t time_key_data = time_keys[packet_idx * 2 + 1];
+ uint32_t data_offset = (time_key_data & 0xFFF) * 4; // lower 12 bits
+ uint32_t data_count = (time_key_data >> 12) + 1;
+
+ const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset);
+
+ uint16_t decode[COMPONENTS];
+ uint16_t decode_next[COMPONENTS];
+
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ decode[i] = data_key[i];
+ decode_next[i] = data_key[i];
+ }
+
+ double next_time = packet_time;
+
+ if (p_time > packet_time) { // If its equal or less, then don't bother
+ if (data_count > 1) {
+ //decode forward
+ uint32_t bit_width[COMPONENTS];
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ bit_width[i] = (data_key[COMPONENTS] >> (i * 4)) & 0xF;
}
- tt->transforms.remove(i);
- i--;
+ uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1;
- } else {
- prev_erased = false;
- norm = Vector3();
+ AnimationCompressionBufferBitsRead buffer;
+
+ buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1];
+
+ for (uint32_t i = 1; i < data_count; i++) {
+ uint32_t frame_delta = buffer.read(frame_bit_width);
+ base_frame += frame_delta;
+
+ for (uint32_t j = 0; j < COMPONENTS; j++) {
+ if (bit_width[j] == 0) {
+ continue; // do none
+ }
+ uint32_t valueu = buffer.read(bit_width[j] + 1);
+ bool sign = valueu & (1 << bit_width[j]);
+ int16_t value = valueu & ((1 << bit_width[j]) - 1);
+ if (sign) {
+ value = -value - 1;
+ }
+
+ decode_next[j] += value;
+ }
+
+ next_time = double(base_frame) * frame_to_sec + page_base_time;
+ if (p_time < next_time) {
+ break;
+ }
+
+ packet_time = next_time;
+
+ for (uint32_t j = 0; j < COMPONENTS; j++) {
+ decode[j] = decode_next[j];
+ }
+
+ if (key_index) {
+ (*key_index)++;
+ }
+ }
+ }
+
+ if (p_time > next_time) { // > instead of >= because if its equal, then it will be properly interpolated anyway
+ // So, the last frame found still has a time that is less than the required frame,
+ // will have to interpolate with the first frame of the next timekey.
+
+ if ((uint32_t)packet_idx < time_key_count - 1) { // Sanity check but should not matter much, otherwise current next packet is last packet
+
+ uint16_t time_key_data_next = time_keys[(packet_idx + 1) * 2 + 1];
+ uint32_t data_offset_next = (time_key_data_next & 0xFFF) * 4; // Lower 12 bits
+
+ const uint16_t *data_key_next = (const uint16_t *)(data_keys_base + data_offset_next);
+ base_frame = time_keys[(packet_idx + 1) * 2 + 0];
+ next_time = double(base_frame) * frame_to_sec + page_base_time;
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ decode_next[i] = data_key_next[i];
+ }
+ }
}
}
+
+ r_current_time = packet_time;
+ r_next_time = next_time;
+
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ r_current_value[i] = decode[i];
+ r_next_value[i] = decode_next[i];
+ }
+
+ return true;
}
-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);
+template <uint32_t COMPONENTS>
+void Animation::_get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List<int> *r_indices) const {
+ ERR_FAIL_COND(!compression.enabled);
+ ERR_FAIL_UNSIGNED_INDEX(p_compressed_track, compression.bounds.size());
+
+ double frame_to_sec = 1.0 / double(compression.fps);
+ uint32_t key_index = 0;
+
+ for (uint32_t p = 0; p < compression.pages.size(); p++) {
+ if (compression.pages[p].time_offset >= p_time + p_delta) {
+ // Page beyond range
+ return;
+ }
+
+ // Page within range
+
+ uint32_t page_index = p;
+
+ double page_base_time = compression.pages[page_index].time_offset;
+ const uint8_t *page_data = compression.pages[page_index].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+
+ for (uint32_t i = 0; i < time_key_count; i++) {
+ uint32_t f = time_keys[i * 2 + 0];
+ double frame_time = f * frame_to_sec + page_base_time;
+ if (frame_time >= p_time + p_delta) {
+ return;
+ } else if (frame_time >= p_time) {
+ r_indices->push_back(key_index);
+ }
+
+ key_index++;
+
+ const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]];
+
+ uint16_t time_key_data = time_keys[i * 2 + 1];
+ uint32_t data_offset = (time_key_data & 0xFFF) * 4; // lower 12 bits
+ uint32_t data_count = (time_key_data >> 12) + 1;
+
+ const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset);
+
+ if (data_count > 1) {
+ //decode forward
+ uint32_t bit_width[COMPONENTS];
+ for (uint32_t j = 0; j < COMPONENTS; j++) {
+ bit_width[j] = (data_key[COMPONENTS] >> (j * 4)) & 0xF;
+ }
+
+ uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1;
+
+ AnimationCompressionBufferBitsRead buffer;
+
+ buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1];
+
+ for (uint32_t j = 1; j < data_count; j++) {
+ uint32_t frame_delta = buffer.read(frame_bit_width);
+ f += frame_delta;
+
+ frame_time = f * frame_to_sec + page_base_time;
+ if (frame_time >= p_time + p_delta) {
+ return;
+ } else if (frame_time >= p_time) {
+ r_indices->push_back(key_index);
+ }
+
+ for (uint32_t k = 0; k < COMPONENTS; k++) {
+ if (bit_width[k] == 0) {
+ continue; // do none
+ }
+ buffer.read(bit_width[k] + 1); // skip
+ }
+
+ key_index++;
+ }
+ }
}
}
}
+int Animation::_get_compressed_key_count(uint32_t p_compressed_track) const {
+ ERR_FAIL_COND_V(!compression.enabled, -1);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), -1);
+
+ int key_count = 0;
+
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ const uint8_t *page_data = compression.pages[i].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+
+ for (uint32_t j = 0; j < time_key_count; j++) {
+ key_count += (time_keys[j * 2 + 1] >> 12) + 1;
+ }
+ }
+
+ return key_count;
+}
+
+Quaternion Animation::_uncompress_quaternion(const Vector3i &p_value) const {
+ Vector3 axis = Vector3::octahedron_decode(Vector2(float(p_value.x) / 65535.0, float(p_value.y) / 65535.0));
+ float angle = (float(p_value.z) / 65535.0) * 2.0 * Math_PI;
+ return Quaternion(axis, angle);
+}
+Vector3 Animation::_uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const {
+ Vector3 pos_norm(float(p_value.x) / 65535.0, float(p_value.y) / 65535.0, float(p_value.z) / 65535.0);
+ return compression.bounds[p_compressed_track].position + pos_norm * compression.bounds[p_compressed_track].size;
+}
+float Animation::_uncompress_blend_shape(const Vector3i &p_value) const {
+ float bsn = float(p_value.x) / 65535.0;
+ return (bsn * 2.0 - 1.0) * float(Compression::BLEND_SHAPE_RANGE);
+}
+
+template <uint32_t COMPONENTS>
+bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false);
+
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ const uint8_t *page_data = compression.pages[i].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+ const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]];
+
+ for (uint32_t j = 0; j < time_key_count; j++) {
+ uint32_t subkeys = (time_keys[j * 2 + 1] >> 12) + 1;
+ if ((uint32_t)p_index < subkeys) {
+ uint16_t data_offset = (time_keys[j * 2 + 1] & 0xFFF) * 4;
+
+ const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset);
+
+ uint16_t frame = time_keys[j * 2 + 0];
+ uint16_t decode[COMPONENTS];
+
+ for (uint32_t k = 0; k < COMPONENTS; k++) {
+ decode[k] = data_key[k];
+ }
+
+ if (p_index > 0) {
+ uint32_t bit_width[COMPONENTS];
+ for (uint32_t k = 0; k < COMPONENTS; k++) {
+ bit_width[k] = (data_key[COMPONENTS] >> (k * 4)) & 0xF;
+ }
+ uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1;
+
+ AnimationCompressionBufferBitsRead buffer;
+ buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1];
+
+ for (int k = 0; k < p_index; k++) {
+ uint32_t frame_delta = buffer.read(frame_bit_width);
+ frame += frame_delta;
+ for (uint32_t l = 0; l < COMPONENTS; l++) {
+ if (bit_width[l] == 0) {
+ continue; // do none
+ }
+ uint32_t valueu = buffer.read(bit_width[l] + 1);
+ bool sign = valueu & (1 << bit_width[l]);
+ int16_t value = valueu & ((1 << bit_width[l]) - 1);
+ if (sign) {
+ value = -value - 1;
+ }
+
+ decode[l] += value;
+ }
+ }
+ }
+
+ r_time = compression.pages[i].time_offset + double(frame) / double(compression.fps);
+ for (uint32_t l = 0; l < COMPONENTS; l++) {
+ r_value[l] = decode[l];
+ }
+
+ return true;
+
+ } else {
+ p_index -= subkeys;
+ }
+ }
+ }
+
+ return false;
+}
+
Animation::Animation() {}
Animation::~Animation() {
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 9a410bd566..510d6c8323 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -32,6 +32,7 @@
#define ANIMATION_H
#include "core/io/resource.h"
+#include "core/templates/local_vector.h"
#define ANIM_MIN_LENGTH 0.001
@@ -42,7 +43,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,
@@ -60,7 +64,12 @@ public:
UPDATE_DISCRETE,
UPDATE_TRIGGER,
UPDATE_CAPTURE,
+ };
+ enum LoopMode {
+ LOOP_NONE,
+ LOOP_LINEAR,
+ LOOP_PINGPONG,
};
private:
@@ -86,21 +95,41 @@ 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;
+ int32_t compressed_track = -1;
+ PositionTrack() { type = TYPE_POSITION_3D; }
+ };
+
+ /* ROTATION TRACK */
+
+ struct RotationTrack : public Track {
+ Vector<TKey<Quaternion>> rotations;
+ int32_t compressed_track = -1;
+ RotationTrack() { type = TYPE_ROTATION_3D; }
};
- // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
- const int32_t TRANSFORM_TRACK_SIZE = 12;
+ /* SCALE TRACK */
- /* TRANSFORM TRACK */
+ struct ScaleTrack : public Track {
+ Vector<TKey<Vector3>> scales;
+ int32_t compressed_track = -1;
+ ScaleTrack() { type = TYPE_SCALE_3D; }
+ };
- struct TransformTrack : public Track {
- Vector<TKey<TransformKey>> transforms;
+ /* BLEND SHAPE TRACK */
- TransformTrack() { type = TYPE_TRANSFORM3D; }
+ struct BlendShapeTrack : public Track {
+ Vector<TKey<float>> blend_shapes;
+ int32_t compressed_track = -1;
+ BlendShapeTrack() { type = TYPE_BLEND_SHAPE; }
};
/* PROPERTY VALUE TRACK */
@@ -184,23 +213,21 @@ private:
int _insert(double p_time, T &p_keys, const V &p_value);
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;
+ inline int _find(const Vector<K> &p_keys, double p_time, bool p_backward = false) 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;
_FORCE_INLINE_ real_t _cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const;
template <class T>
- _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
+ _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const;
template <class T>
_FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const;
@@ -210,22 +237,94 @@ private:
double length = 1.0;
real_t step = 0.1;
- bool loop = false;
+ LoopMode loop_mode = LOOP_NONE;
+ int pingponged = 0;
+
+ /* Animation compression page format (version 1):
+ *
+ * Animation uses bitwidth based compression separated into small pages. The intention is that pages fit easily in the cache, so decoding is cache efficient.
+ * The page-based nature also makes future animation streaming from disk possible.
+ *
+ * Actual format:
+ *
+ * num_compressed_tracks = bounds.size()
+ * header : (x num_compressed_tracks)
+ * -------
+ * timeline_keys_offset : uint32_t - offset to time keys
+ * timeline_size : uint32_t - amount of time keys
+ * data_keys_offset : uint32_t offset to key data
+ *
+ * time key (uint32_t):
+ * ------------------
+ * frame : bits 0-15 - time offset of key, computed as: page.time_offset + frame * (1.0/fps)
+ * data_key_offset : bits 16-27 - offset to key data, computed as: data_keys_offset * 4 + data_key_offset
+ * data_key_count : bits 28-31 - amount of data keys pointed to, computed as: data_key_count+1 (max 16)
+ *
+ * data key:
+ * ---------
+ * X / Blend Shape : uint16_t - X coordinate of XYZ vector key, or Blend Shape value. If Blend shape, Y and Z are not present and can be ignored.
+ * Y : uint16_t
+ * Z : uint16_t
+ * If data_key_count+1 > 1 (if more than 1 key is stored):
+ * data_bitwidth : uint16_t - This is only present if data_key_count > 1. Contains delta bitwidth information.
+ * X / Blend Shape delta bitwidth: bits 0-3 -
+ * if 0, nothing is present for X (use the first key-value for subsequent keys),
+ * else assume the number of bits present for each element (+ 1 for sign). Assumed always 16 bits, delta max signed 15 bits, with underflow and overflow supported.
+ * Y delta bitwidth : bits 4-7
+ * Z delta bitwidth : bits 8-11
+ * FRAME delta bitwidth : 12-15 bits - always present (obviously), actual bitwidth is FRAME+1
+ * Data key is 4 bytes long for Blend Shapes, 8 bytes long for pos/rot/scale.
+ *
+ * delta keys:
+ * -----------
+ * Compressed format is packed in the following format after the data key, containing delta keys one after the next in a tightly bit packed fashion.
+ * FRAME bits -> X / Blend Shape Bits (if bitwidth > 0) -> Y Bits (if not Blend Shape and Y Bitwidth > 0) -> Z Bits (if not Blend Shape and Z Bitwidth > 0)
+ *
+ * data key format:
+ * ----------------
+ * Decoding keys means starting from the base key and going key by key applying deltas until the proper position is reached needed for interpolation.
+ * Resulting values are uint32_t
+ * data for X / Blend Shape, Y and Z must be normalized first: unorm = float(data) / 65535.0
+ * **Blend Shape**: (unorm * 2.0 - 1.0) * Compression::BLEND_SHAPE_RANGE
+ * **Pos/Scale**: unorm_vec3 * bounds[track].size + bounds[track].position
+ * **Rotation**: Quaternion(Vector3::octahedron_decode(unorm_vec3.xy),unorm_vec3.z * Math_PI * 2.0)
+ * **Frame**: page.time_offset + frame * (1.0/fps)
+ */
+
+ struct Compression {
+ enum {
+ MAX_DATA_TRACK_SIZE = 16384,
+ BLEND_SHAPE_RANGE = 8, // - 8.0 to 8.0
+ FORMAT_VERSION = 1
+ };
+ struct Page {
+ Vector<uint8_t> data;
+ double time_offset;
+ };
+
+ uint32_t fps = 120;
+ LocalVector<Page> pages;
+ LocalVector<AABB> bounds; //used by position and scale tracks (which contain index to track and index to bounds).
+ bool enabled = false;
+ } compression;
+
+ Vector3i _compress_key(uint32_t p_track, const AABB &p_bounds, int32_t p_key = -1, float p_time = 0.0);
+ bool _rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const;
+ bool _pos_scale_interpolate_compressed(uint32_t p_compressed_track, double p_time, Vector3 &r_ret) const;
+ bool _blend_shape_interpolate_compressed(uint32_t p_compressed_track, double p_time, float &r_ret) const;
+ template <uint32_t COMPONENTS>
+ bool _fetch_compressed(uint32_t p_compressed_track, double p_time, Vector3i &r_current_value, double &r_current_time, Vector3i &r_next_value, double &r_next_time, uint32_t *key_index = nullptr) const;
+ template <uint32_t COMPONENTS>
+ bool _fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const;
+ int _get_compressed_key_count(uint32_t p_compressed_track) const;
+ template <uint32_t COMPONENTS>
+ void _get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List<int> *r_indices) const;
+ _FORCE_INLINE_ Quaternion _uncompress_quaternion(const Vector3i &p_value) const;
+ _FORCE_INLINE_ Vector3 _uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const;
+ _FORCE_INLINE_ float _uncompress_blend_shape(const Vector3i &p_value) const;
// 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 +346,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);
@@ -268,8 +374,7 @@ public:
void track_set_path(int p_track, const NodePath &p_path);
NodePath track_get_path(int p_track) const;
- int find_track(const NodePath &p_path) const;
- // transform
+ int find_track(const NodePath &p_path, const TrackType p_type) const;
void track_move_up(int p_track);
void track_move_down(int p_track);
@@ -293,9 +398,24 @@ public:
Variant track_get_key_value(int p_track, int p_key_idx) const;
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;
+ bool track_is_compressed(int p_track) 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;
- 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;
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
@@ -324,26 +444,24 @@ 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_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;
- void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const;
StringName method_track_get_name(int p_track, int p_key_idx) const;
void copy_track(int p_track, Ref<Animation> p_to_animation);
- void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
void set_length(real_t p_length);
real_t get_length() const;
- void set_loop(bool p_enabled);
- bool has_loop() const;
+ void set_loop_mode(LoopMode p_loop_mode);
+ LoopMode get_loop_mode() const;
void set_step(real_t p_step);
real_t get_step() const;
@@ -351,6 +469,7 @@ public:
void clear();
void optimize(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);
+ void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests
Animation();
~Animation();
@@ -359,5 +478,6 @@ public:
VARIANT_ENUM_CAST(Animation::TrackType);
VARIANT_ENUM_CAST(Animation::InterpolationType);
VARIANT_ENUM_CAST(Animation::UpdateMode);
+VARIANT_ENUM_CAST(Animation::LoopMode);
#endif
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index d018103e64..d3fab802c5 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -299,7 +299,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (loop_format != AudioStreamSample::LOOP_DISABLED && offset < loop_begin_fp) {
/* loopstart reached */
- if (loop_format == AudioStreamSample::LOOP_PING_PONG) {
+ if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_begin_fp + (loop_begin_fp - offset);
increment = -increment;
@@ -320,7 +320,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (loop_format != AudioStreamSample::LOOP_DISABLED && offset >= loop_end_fp) {
/* loopend reached */
- if (loop_format == AudioStreamSample::LOOP_PING_PONG) {
+ if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_end_fp - (offset - loop_end_fp);
increment = -increment;
@@ -636,7 +636,7 @@ void AudioStreamSample::_bind_methods() {
ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
@@ -650,7 +650,7 @@ void AudioStreamSample::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_DISABLED);
BIND_ENUM_CONSTANT(LOOP_FORWARD);
- BIND_ENUM_CONSTANT(LOOP_PING_PONG);
+ BIND_ENUM_CONSTANT(LOOP_PINGPONG);
BIND_ENUM_CONSTANT(LOOP_BACKWARD);
}
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 24198e3c98..0eb34be9bf 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -92,7 +92,7 @@ public:
enum LoopMode {
LOOP_DISABLED,
LOOP_FORWARD,
- LOOP_PING_PONG,
+ LOOP_PINGPONG,
LOOP_BACKWARD
};
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index 49ed9dcb82..16f1507c34 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -674,7 +674,7 @@ void BitMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask);
ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0));
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
BitMap::BitMap() {}
diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp
index b633196424..0df372ea1b 100644
--- a/scene/resources/camera_effects.cpp
+++ b/scene/resources/camera_effects.cpp
@@ -149,7 +149,7 @@ void CameraEffects::_validate_property(PropertyInfo &property) const {
if ((!dof_blur_far_enabled && (property.name == "dof_blur_far_distance" || property.name == "dof_blur_far_transition")) ||
(!dof_blur_near_enabled && (property.name == "dof_blur_near_distance" || property.name == "dof_blur_near_transition")) ||
(!override_exposure_enabled && property.name == "override_exposure")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp
index 3fed700383..03854683bd 100644
--- a/scene/resources/concave_polygon_shape_3d.cpp
+++ b/scene/resources/concave_polygon_shape_3d.cpp
@@ -108,7 +108,7 @@ void ConcavePolygonShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_backface_collision_enabled", "enabled"), &ConcavePolygonShape3D::set_backface_collision_enabled);
ClassDB::bind_method(D_METHOD("is_backface_collision_enabled"), &ConcavePolygonShape3D::is_backface_collision_enabled);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_faces", "get_faces");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "backface_collision"), "set_backface_collision_enabled", "is_backface_collision_enabled");
}
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 9dc76dcf44..b530a72033 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -522,7 +522,7 @@ void Curve::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED));
@@ -1006,7 +1006,7 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data"), &Curve2D::_set_data);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
Curve2D::Curve2D() {
@@ -1699,7 +1699,7 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data"), &Curve3D::_set_data);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_GROUP("Up Vector", "up_vector_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
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 54bb7a82cf..a1d76ef352 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -147,6 +147,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Color control_font_lower_color = Color(0.63, 0.63, 0.63);
Color control_font_low_color = Color(0.69, 0.69, 0.69);
Color control_font_hover_color = Color(0.94, 0.94, 0.94);
+ Color control_font_focus_color = Color(0.94, 0.94, 0.94);
Color control_font_disabled_color = Color(0.9, 0.9, 0.9, 0.2);
Color control_font_pressed_color = Color(1, 1, 1);
@@ -185,6 +186,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "Button", control_font_color);
theme->set_color("font_pressed_color", "Button", control_font_pressed_color);
theme->set_color("font_hover_color", "Button", control_font_hover_color);
+ theme->set_color("font_focus_color", "Button", control_font_focus_color);
theme->set_color("font_hover_pressed_color", "Button", control_font_pressed_color);
theme->set_color("font_disabled_color", "Button", control_font_disabled_color);
theme->set_color("font_outline_color", "Button", Color(1, 1, 1));
@@ -193,6 +195,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_pressed_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_hover_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_hover_pressed_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 1));
theme->set_constant("hseparation", "Button", 2 * scale);
@@ -207,6 +210,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "LinkButton", control_font_color);
theme->set_color("font_pressed_color", "LinkButton", control_font_pressed_color);
theme->set_color("font_hover_color", "LinkButton", control_font_hover_color);
+ theme->set_color("font_focus_color", "LinkButton", control_font_focus_color);
theme->set_color("font_outline_color", "LinkButton", Color(1, 1, 1));
theme->set_constant("outline_size", "LinkButton", 0);
@@ -245,6 +249,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "OptionButton", control_font_color);
theme->set_color("font_pressed_color", "OptionButton", control_font_pressed_color);
theme->set_color("font_hover_color", "OptionButton", control_font_hover_color);
+ theme->set_color("font_focus_color", "OptionButton", control_font_focus_color);
theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color);
theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
@@ -266,6 +271,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "MenuButton", control_font_color);
theme->set_color("font_pressed_color", "MenuButton", control_font_pressed_color);
theme->set_color("font_hover_color", "MenuButton", control_font_hover_color);
+ theme->set_color("font_focus_color", "MenuButton", control_font_focus_color);
theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3));
theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1));
@@ -308,6 +314,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_pressed_color", "CheckBox", control_font_pressed_color);
theme->set_color("font_hover_color", "CheckBox", control_font_hover_color);
theme->set_color("font_hover_pressed_color", "CheckBox", control_font_pressed_color);
+ theme->set_color("font_focus_color", "CheckBox", control_font_focus_color);
theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1));
@@ -347,6 +354,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_pressed_color", "CheckButton", control_font_pressed_color);
theme->set_color("font_hover_color", "CheckButton", control_font_hover_color);
theme->set_color("font_hover_pressed_color", "CheckButton", control_font_pressed_color);
+ theme->set_color("font_focus_color", "CheckButton", control_font_focus_color);
theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1));
@@ -785,30 +793,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("close_bg_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("close_bg_highlight", "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
@@ -867,6 +875,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_focus_color", "ColorPickerButton", Color(1, 1, 1, 1));
theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
@@ -931,7 +940,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
- theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
+ theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale);
theme->set_constant("line_separation", "RichTextLabel", 0 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
@@ -1030,7 +1039,6 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
dynamic_font_data.instantiate();
dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
dynamic_font->add_data(dynamic_font_data);
- dynamic_font->set_base_size(default_font_size);
default_font = dynamic_font;
}
diff --git a/scene/resources/default_theme/overbright_indicator.png b/scene/resources/default_theme/overbright_indicator.png
index 89f800c230..e13f15dd02 100644
--- a/scene/resources/default_theme/overbright_indicator.png
+++ b/scene/resources/default_theme/overbright_indicator.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 6a556c1112..57ff9a5325 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -50,6 +50,10 @@ static const unsigned char checked_disabled_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char checker_bg_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char close_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -218,10 +222,6 @@ static const unsigned char indeterminate_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xfe, 0xfe, 0xfe, 0x98, 0x4d, 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x77, 0x5e, 0x7f, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0xd1, 0x4, 0x14, 0xd, 0xc5, 0xfa, 0xff, 0x9f, 0x8d, 0x9c, 0x6c, 0x9a, 0xb4, 0xe9, 0x2e, 0xcf, 0x42, 0x9e, 0xcb, 0x32, 0xe4, 0x0, 0xc9, 0xb7, 0x8, 0xc1, 0x19, 0x40, 0x60, 0xc9, 0x2d, 0xe1, 0x0, 0x6, 0xc8, 0x45, 0x6b, 0x4b, 0xb, 0xa3, 0x1, 0x89, 0x6e, 0x57, 0x2a, 0x64, 0xe0, 0x73, 0xed, 0x50, 0xb3, 0x9f, 0xc3, 0x7e, 0xf7, 0x5, 0x7f, 0x6f, 0xc, 0x67, 0x9f, 0xc3, 0xe2, 0x39, 0xc, 0x52, 0xec, 0xd3, 0xd7, 0x4, 0xb3, 0xcf, 0xbd, 0x3a, 0x0, 0xa0, 0xa2, 0x8, 0xbc, 0xf6, 0x84, 0x3a, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
-static const unsigned char indeterminate_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x3c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0x9e, 0x9e, 0x9e, 0x8c, 0x93, 0x80, 0x95, 0x0, 0x0, 0x0, 0x14, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6c, 0xb9, 0x8d, 0x1e, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xd1, 0xe, 0x80, 0x20, 0x8, 0x85, 0xe1, 0x4, 0xd4, 0x30, 0x51, 0xb6, 0xde, 0xff, 0x5d, 0x23, 0x97, 0xad, 0xa5, 0xad, 0xff, 0xf2, 0xbb, 0x90, 0xe3, 0xb2, 0xc, 0x39, 0x40, 0xf2, 0x2d, 0x42, 0x70, 0x6, 0x10, 0x58, 0x6b, 0x4b, 0x39, 0x80, 0x1, 0x72, 0x91, 0xdc, 0x92, 0xc2, 0x68, 0x40, 0x2a, 0xdb, 0x95, 0x28, 0x19, 0xf8, 0x9a, 0x3b, 0xe4, 0xea, 0xe7, 0xb0, 0xdf, 0x7d, 0xc1, 0xdf, 0x1b, 0xc3, 0xd9, 0xe7, 0xb0, 0x74, 0xe, 0x83, 0x98, 0xfa, 0xf4, 0x35, 0xc2, 0xec, 0x73, 0xaf, 0xe, 0x57, 0x20, 0x8, 0x2c, 0x1a, 0x56, 0xe5, 0x32, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
static const unsigned char line_edit_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
@@ -275,7 +275,7 @@ static const unsigned char option_button_pressed_mirrored_png[] = {
};
static const unsigned char overbright_indicator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x1, 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x2a, 0x2d, 0xe, 0x16, 0x11, 0x75, 0xc8, 0x50, 0x9d, 0x2c, 0x8a, 0x8a, 0x38, 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0xa, 0xad, 0x3a, 0x98, 0x5c, 0xfa, 0x21, 0x34, 0x69, 0x48, 0x52, 0x5c, 0x1c, 0x5, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x7, 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x5, 0x41, 0xf0, 0x3, 0xc4, 0xc5, 0xd5, 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x5a, 0x68, 0x11, 0xe3, 0xc1, 0x71, 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x3, 0x84, 0x6a, 0x91, 0x69, 0x56, 0xdb, 0x18, 0xa0, 0xe9, 0xb6, 0x99, 0x8c, 0xc7, 0xc4, 0x74, 0x66, 0x45, 0xc, 0xbc, 0xa2, 0x13, 0x3, 0x8, 0xa1, 0x17, 0xa3, 0x32, 0xb3, 0x8c, 0x59, 0x49, 0x4a, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0x8, 0xa9, 0x59, 0x8b, 0x1, 0x3e, 0x91, 0x78, 0x86, 0x19, 0xa6, 0x4d, 0xbc, 0x4e, 0x3c, 0xb5, 0x69, 0x1b, 0x9c, 0xf7, 0x89, 0xc3, 0xac, 0x20, 0xab, 0xc4, 0xe7, 0xc4, 0x23, 0x26, 0x5d, 0x90, 0xf8, 0x91, 0xeb, 0x4a, 0x9d, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x85, 0x95, 0x16, 0x66, 0x5, 0x53, 0x23, 0x9e, 0x24, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xba, 0xce, 0x2a, 0xe7, 0x2d, 0xce, 0x5a, 0xb1, 0xcc, 0x1a, 0xf7, 0xe4, 0x2f, 0xc, 0x66, 0xf5, 0xe5, 0x25, 0xae, 0xd3, 0x1c, 0x44, 0x1c, 0xb, 0x58, 0x84, 0x4, 0x11, 0xa, 0xca, 0xd8, 0x40, 0x11, 0x36, 0xa2, 0xb4, 0xea, 0xa4, 0x58, 0x48, 0xd2, 0x7e, 0xcc, 0xc3, 0xdf, 0xef, 0xfa, 0x25, 0x72, 0x29, 0xe4, 0xda, 0x0, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x9b, 0x18, 0xaf, 0x27, 0x5, 0x63, 0x40, 0xfb, 0x8b, 0xe3, 0x7c, 0xc, 0x1, 0x81, 0x5d, 0xa0, 0x56, 0x71, 0x9c, 0xef, 0x63, 0xc7, 0xa9, 0x9d, 0x0, 0xfe, 0x67, 0xe0, 0x4a, 0x6f, 0xfa, 0x4b, 0x55, 0x60, 0xfa, 0x93, 0xf4, 0x4a, 0x53, 0x8b, 0x1c, 0x1, 0xdd, 0xdb, 0xc0, 0xc5, 0x75, 0x53, 0x53, 0xf6, 0x80, 0xcb, 0x1d, 0xa0, 0xef, 0xc9, 0x90, 0x4d, 0xd9, 0x95, 0xfc, 0x34, 0x85, 0x5c, 0xe, 0x78, 0x3f, 0xa3, 0x6f, 0xca, 0x0, 0x3d, 0xb7, 0x40, 0xd7, 0x6a, 0xbd, 0xb7, 0xc6, 0x3e, 0x4e, 0x1f, 0x80, 0x14, 0x75, 0x95, 0xb8, 0x1, 0xe, 0xe, 0x81, 0xe1, 0x3c, 0x65, 0xaf, 0x79, 0xbc, 0xbb, 0xa3, 0xb5, 0xb7, 0x7f, 0xcf, 0x34, 0xfa, 0xfb, 0x1, 0x8e, 0x80, 0x72, 0xb2, 0xed, 0x78, 0xfa, 0x7b, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x15, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x63, 0x63, 0x66, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4c, 0x39, 0x3a, 0xe, 0x0, 0x0, 0x0, 0x6, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x80, 0x2c, 0x16, 0xc1, 0x6d, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6, 0x61, 0x66, 0xb8, 0x7d, 0x0, 0x0, 0x0, 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x1, 0x46, 0x65, 0x17, 0x17, 0x30, 0x43, 0xc8, 0x4, 0x50, 0x88, 0x1c, 0x52, 0x1, 0x0, 0x2, 0x40, 0x14, 0xbb, 0x70, 0x8b, 0x40, 0xff, 0x2c, 0x18, 0xbe, 0xc6, 0xed, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0x14, 0xa, 0x85, 0xbd, 0xb0, 0x13, 0xfc, 0x71, 0x1, 0xca, 0xf, 0x19, 0x62, 0x24, 0xd6, 0x8, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0xcd, 0x4b, 0xe, 0x80, 0x20, 0xc, 0x45, 0xd1, 0x9b, 0xea, 0xbe, 0x40, 0x97, 0x87, 0x8b, 0xae, 0x13, 0x34, 0x88, 0x1f, 0xa, 0x9d, 0xf8, 0x92, 0xe, 0xcf, 0x2d, 0x80, 0xda, 0x4f, 0x14, 0x26, 0x5, 0x49, 0x14, 0x53, 0xcb, 0x42, 0x58, 0xe, 0xbc, 0x51, 0xcd, 0x85, 0x9b, 0x81, 0x16, 0xfe, 0xc, 0x58, 0xf0, 0x6b, 0xc0, 0x8a, 0x1f, 0x3, 0x3d, 0xf8, 0x16, 0xe8, 0xc5, 0x97, 0x40, 0x81, 0x53, 0x9b, 0x55, 0x81, 0x91, 0xcf, 0x67, 0x20, 0xc6, 0x75, 0xe8, 0x73, 0x9e, 0xc, 0x7f, 0xce, 0x73, 0x61, 0x80, 0xd9, 0x83, 0x7f, 0xb0, 0x1d, 0xec, 0x30, 0xf0, 0x78, 0x5a, 0x7, 0xa8, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
static const unsigned char panel_bg_png[] = {
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 9a3f081a8b..4c25ed589b 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -782,7 +782,7 @@ void Environment::_update_fog() {
// Volumetric Fog
void Environment::_update_volumetric_fog() {
- RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount);
+ RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_albedo, volumetric_fog_emission, volumetric_fog_emission_energy, volumetric_fog_anisotropy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, volumetric_fog_temporal_reproject, volumetric_fog_temporal_reproject_amount, volumetric_fog_ambient_inject);
}
void Environment::set_volumetric_fog_enabled(bool p_enable) {
@@ -795,26 +795,39 @@ bool Environment::is_volumetric_fog_enabled() const {
return volumetric_fog_enabled;
}
void Environment::set_volumetric_fog_density(float p_density) {
- p_density = CLAMP(p_density, 0.0000001, 1.0);
volumetric_fog_density = p_density;
_update_volumetric_fog();
}
float Environment::get_volumetric_fog_density() const {
return volumetric_fog_density;
}
-void Environment::set_volumetric_fog_light(Color p_color) {
- volumetric_fog_light = p_color;
+void Environment::set_volumetric_fog_albedo(Color p_color) {
+ volumetric_fog_albedo = p_color;
_update_volumetric_fog();
}
-Color Environment::get_volumetric_fog_light() const {
- return volumetric_fog_light;
+Color Environment::get_volumetric_fog_albedo() const {
+ return volumetric_fog_albedo;
}
-void Environment::set_volumetric_fog_light_energy(float p_begin) {
- volumetric_fog_light_energy = p_begin;
+void Environment::set_volumetric_fog_emission(Color p_color) {
+ volumetric_fog_emission = p_color;
_update_volumetric_fog();
}
-float Environment::get_volumetric_fog_light_energy() const {
- return volumetric_fog_light_energy;
+Color Environment::get_volumetric_fog_emission() const {
+ return volumetric_fog_emission;
+}
+void Environment::set_volumetric_fog_emission_energy(float p_begin) {
+ volumetric_fog_emission_energy = p_begin;
+ _update_volumetric_fog();
+}
+float Environment::get_volumetric_fog_emission_energy() const {
+ return volumetric_fog_emission_energy;
+}
+void Environment::set_volumetric_fog_anisotropy(float p_anisotropy) {
+ volumetric_fog_anisotropy = p_anisotropy;
+ _update_volumetric_fog();
+}
+float Environment::get_volumetric_fog_anisotropy() const {
+ return volumetric_fog_anisotropy;
}
void Environment::set_volumetric_fog_length(float p_length) {
volumetric_fog_length = p_length;
@@ -824,6 +837,7 @@ float Environment::get_volumetric_fog_length() const {
return volumetric_fog_length;
}
void Environment::set_volumetric_fog_detail_spread(float p_detail_spread) {
+ p_detail_spread = CLAMP(p_detail_spread, 0.5, 6.0);
volumetric_fog_detail_spread = p_detail_spread;
_update_volumetric_fog();
}
@@ -838,6 +852,13 @@ void Environment::set_volumetric_fog_gi_inject(float p_gi_inject) {
float Environment::get_volumetric_fog_gi_inject() const {
return volumetric_fog_gi_inject;
}
+void Environment::set_volumetric_fog_ambient_inject(float p_ambient_inject) {
+ volumetric_fog_ambient_inject = p_ambient_inject;
+ _update_volumetric_fog();
+}
+float Environment::get_volumetric_fog_ambient_inject() const {
+ return volumetric_fog_ambient_inject;
+}
void Environment::set_volumetric_fog_temporal_reprojection_enabled(bool p_enable) {
volumetric_fog_temporal_reproject = p_enable;
@@ -896,7 +917,7 @@ float Environment::get_adjustment_saturation() const {
void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correction) {
adjustment_color_correction = p_color_correction;
- Ref<GradientTexture> grad_tex = p_color_correction;
+ Ref<GradientTexture1D> grad_tex = p_color_correction;
if (grad_tex.is_valid()) {
if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) {
grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment));
@@ -933,39 +954,39 @@ void Environment::_update_adjustment() {
void Environment::_validate_property(PropertyInfo &property) const {
if (property.name == "sky" || property.name == "sky_custom_fov" || property.name == "sky_rotation" || property.name == "ambient_light/sky_contribution") {
if (bg_mode != BG_SKY && ambient_source != AMBIENT_SOURCE_SKY && reflection_source != REFLECTION_SOURCE_SKY) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "fog_aerial_perspective") {
if (bg_mode != BG_SKY) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
if (property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
if (property.name == "background_color") {
if (bg_mode != BG_COLOR && ambient_source != AMBIENT_SOURCE_COLOR) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "background_canvas_max_layer") {
if (bg_mode != BG_CANVAS) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
if (property.name == "background_camera_feed_id") {
if (bg_mode != BG_CAMERA_FEED) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -997,7 +1018,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
String enabled = prefix + "enabled";
if (property.name.begins_with(prefix) && property.name != enabled && !bool(get(enabled))) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
return;
}
@@ -1010,7 +1031,7 @@ void Environment::_validate_property(PropertyInfo &property) const {
String prefix = String(*prefixes);
if (property.name.begins_with(prefix)) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
return;
}
@@ -1291,22 +1312,28 @@ 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);
- ClassDB::bind_method(D_METHOD("set_volumetric_fog_light", "color"), &Environment::set_volumetric_fog_light);
- ClassDB::bind_method(D_METHOD("get_volumetric_fog_light"), &Environment::get_volumetric_fog_light);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_emission", "color"), &Environment::set_volumetric_fog_emission);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_emission"), &Environment::get_volumetric_fog_emission);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_albedo", "color"), &Environment::set_volumetric_fog_albedo);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_albedo"), &Environment::get_volumetric_fog_albedo);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_density", "density"), &Environment::set_volumetric_fog_density);
ClassDB::bind_method(D_METHOD("get_volumetric_fog_density"), &Environment::get_volumetric_fog_density);
- ClassDB::bind_method(D_METHOD("set_volumetric_fog_light_energy", "begin"), &Environment::set_volumetric_fog_light_energy);
- ClassDB::bind_method(D_METHOD("get_volumetric_fog_light_energy"), &Environment::get_volumetric_fog_light_energy);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_emission_energy", "begin"), &Environment::set_volumetric_fog_emission_energy);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_emission_energy"), &Environment::get_volumetric_fog_emission_energy);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_anisotropy", "anisotropy"), &Environment::set_volumetric_fog_anisotropy);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_anisotropy"), &Environment::get_volumetric_fog_anisotropy);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_length", "length"), &Environment::set_volumetric_fog_length);
ClassDB::bind_method(D_METHOD("get_volumetric_fog_length"), &Environment::get_volumetric_fog_length);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_detail_spread", "detail_spread"), &Environment::set_volumetric_fog_detail_spread);
ClassDB::bind_method(D_METHOD("get_volumetric_fog_detail_spread"), &Environment::get_volumetric_fog_detail_spread);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_gi_inject", "gi_inject"), &Environment::set_volumetric_fog_gi_inject);
ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject);
+ ClassDB::bind_method(D_METHOD("set_volumetric_fog_ambient_inject", "enabled"), &Environment::set_volumetric_fog_ambient_inject);
+ ClassDB::bind_method(D_METHOD("get_volumetric_fog_ambient_inject"), &Environment::get_volumetric_fog_ambient_inject);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_enabled", "enabled"), &Environment::set_volumetric_fog_temporal_reprojection_enabled);
ClassDB::bind_method(D_METHOD("is_volumetric_fog_temporal_reprojection_enabled"), &Environment::is_volumetric_fog_temporal_reprojection_enabled);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_temporal_reprojection_amount", "temporal_reprojection_amount"), &Environment::set_volumetric_fog_temporal_reprojection_amount);
@@ -1315,11 +1342,14 @@ void Environment::_bind_methods() {
ADD_GROUP("Volumetric Fog", "volumetric_fog_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_enabled"), "set_volumetric_fog_enabled", "is_volumetric_fog_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.00,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_albedo", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_albedo", "get_volumetric_fog_albedo");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_emission", "get_volumetric_fog_emission");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_emission_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_emission_energy", "get_volumetric_fog_emission_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_anisotropy", PROPERTY_HINT_RANGE, "-0.9,0.9,0.01"), "set_volumetric_fog_anisotropy", "get_volumetric_fog_anisotropy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject");
ADD_SUBGROUP("Temporal Reprojection", "volumetric_fog_temporal_reprojection_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_temporal_reprojection_enabled"), "set_volumetric_fog_temporal_reprojection_enabled", "is_volumetric_fog_temporal_reprojection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_temporal_reprojection_amount", PROPERTY_HINT_RANGE, "0.0,0.999,0.001"), "set_volumetric_fog_temporal_reprojection_amount", "get_volumetric_fog_temporal_reprojection_amount");
@@ -1381,11 +1411,6 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT);
-
- BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED);
- BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_LOW);
- BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM);
- BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_HIGH);
}
Environment::Environment() {
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 46842754f4..024bef34de 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -90,13 +90,6 @@ public:
GLOW_BLEND_MODE_MIX,
};
- enum VolumetricFogShadowFilter {
- VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED,
- VOLUMETRIC_FOG_SHADOW_FILTER_LOW,
- VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM,
- VOLUMETRIC_FOG_SHADOW_FILTER_HIGH,
- };
-
private:
RID environment;
@@ -190,12 +183,15 @@ private:
// Volumetric Fog
bool volumetric_fog_enabled = false;
- float volumetric_fog_density = 0.01;
- Color volumetric_fog_light = Color(0.0, 0.0, 0.0);
- float volumetric_fog_light_energy = 1.0;
+ float volumetric_fog_density = 0.05;
+ Color volumetric_fog_albedo = Color(1.0, 1.0, 1.0);
+ Color volumetric_fog_emission = Color(0.0, 0.0, 0.0);
+ float volumetric_fog_emission_energy = 1.0;
+ float volumetric_fog_anisotropy = 0.2;
float volumetric_fog_length = 64.0;
float volumetric_fog_detail_spread = 2.0;
float volumetric_fog_gi_inject = 0.0;
+ float volumetric_fog_ambient_inject = false;
bool volumetric_fog_temporal_reproject = true;
float volumetric_fog_temporal_reproject_amount = 0.9;
void _update_volumetric_fog();
@@ -375,16 +371,22 @@ public:
bool is_volumetric_fog_enabled() const;
void set_volumetric_fog_density(float p_density);
float get_volumetric_fog_density() const;
- void set_volumetric_fog_light(Color p_color);
- Color get_volumetric_fog_light() const;
- void set_volumetric_fog_light_energy(float p_begin);
- float get_volumetric_fog_light_energy() const;
+ void set_volumetric_fog_albedo(Color p_color);
+ Color get_volumetric_fog_albedo() const;
+ void set_volumetric_fog_emission(Color p_color);
+ Color get_volumetric_fog_emission() const;
+ void set_volumetric_fog_emission_energy(float p_begin);
+ float get_volumetric_fog_emission_energy() const;
+ void set_volumetric_fog_anisotropy(float p_anisotropy);
+ float get_volumetric_fog_anisotropy() const;
void set_volumetric_fog_length(float p_length);
float get_volumetric_fog_length() const;
void set_volumetric_fog_detail_spread(float p_detail_spread);
float get_volumetric_fog_detail_spread() const;
void set_volumetric_fog_gi_inject(float p_gi_inject);
float get_volumetric_fog_gi_inject() const;
+ void set_volumetric_fog_ambient_inject(float p_ambient_inject);
+ float get_volumetric_fog_ambient_inject() const;
void set_volumetric_fog_temporal_reprojection_enabled(bool p_enable);
bool is_volumetric_fog_temporal_reprojection_enabled() const;
void set_volumetric_fog_temporal_reprojection_amount(float p_amount);
@@ -413,6 +415,5 @@ VARIANT_ENUM_CAST(Environment::ToneMapper)
VARIANT_ENUM_CAST(Environment::SDFGICascades)
VARIANT_ENUM_CAST(Environment::SDFGIYScale)
VARIANT_ENUM_CAST(Environment::GlowBlendMode)
-VARIANT_ENUM_CAST(Environment::VolumetricFogShadowFilter)
#endif // ENVIRONMENT_H
diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp
new file mode 100644
index 0000000000..978aaca33d
--- /dev/null
+++ b/scene/resources/fog_material.cpp
@@ -0,0 +1,181 @@
+/*************************************************************************/
+/* fog_material.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 "fog_material.h"
+
+#include "core/version.h"
+
+Mutex FogMaterial::shader_mutex;
+RID FogMaterial::shader;
+
+void FogMaterial::set_density(float p_density) {
+ density = p_density;
+ RS::get_singleton()->material_set_param(_get_material(), "density", density);
+}
+
+float FogMaterial::get_density() const {
+ return density;
+}
+
+void FogMaterial::set_albedo(Color p_albedo) {
+ albedo = p_albedo;
+ RS::get_singleton()->material_set_param(_get_material(), "albedo", albedo);
+}
+
+Color FogMaterial::get_albedo() const {
+ return albedo;
+}
+
+void FogMaterial::set_emission(Color p_emission) {
+ emission = p_emission;
+ RS::get_singleton()->material_set_param(_get_material(), "emission", emission);
+}
+
+Color FogMaterial::get_emission() const {
+ return emission;
+}
+
+void FogMaterial::set_height_falloff(float p_falloff) {
+ height_falloff = MAX(p_falloff, 0.0f);
+ RS::get_singleton()->material_set_param(_get_material(), "height_falloff", height_falloff);
+}
+
+float FogMaterial::get_height_falloff() const {
+ return height_falloff;
+}
+
+void FogMaterial::set_edge_fade(float p_edge_fade) {
+ edge_fade = MAX(p_edge_fade, 0.0f);
+ RS::get_singleton()->material_set_param(_get_material(), "edge_fade", edge_fade);
+}
+
+float FogMaterial::get_edge_fade() const {
+ return edge_fade;
+}
+
+void FogMaterial::set_density_texture(const Ref<Texture3D> &p_texture) {
+ density_texture = p_texture;
+ RID tex_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+ RS::get_singleton()->material_set_param(_get_material(), "density_texture", tex_rid);
+}
+
+Ref<Texture3D> FogMaterial::get_density_texture() const {
+ return density_texture;
+}
+
+Shader::Mode FogMaterial::get_shader_mode() const {
+ return Shader::MODE_FOG;
+}
+
+RID FogMaterial::get_shader_rid() const {
+ _update_shader();
+ return shader;
+}
+
+RID FogMaterial::get_rid() const {
+ _update_shader();
+ if (!shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader);
+ shader_set = true;
+ }
+ return _get_material();
+}
+
+void FogMaterial::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_density", "density"), &FogMaterial::set_density);
+ ClassDB::bind_method(D_METHOD("get_density"), &FogMaterial::get_density);
+ ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &FogMaterial::set_albedo);
+ ClassDB::bind_method(D_METHOD("get_albedo"), &FogMaterial::get_albedo);
+ ClassDB::bind_method(D_METHOD("set_emission", "emission"), &FogMaterial::set_emission);
+ ClassDB::bind_method(D_METHOD("get_emission"), &FogMaterial::get_emission);
+ ClassDB::bind_method(D_METHOD("set_height_falloff", "height_falloff"), &FogMaterial::set_height_falloff);
+ ClassDB::bind_method(D_METHOD("get_height_falloff"), &FogMaterial::get_height_falloff);
+ ClassDB::bind_method(D_METHOD("set_edge_fade", "edge_fade"), &FogMaterial::set_edge_fade);
+ ClassDB::bind_method(D_METHOD("get_edge_fade"), &FogMaterial::get_edge_fade);
+ ClassDB::bind_method(D_METHOD("set_density_texture", "density_texture"), &FogMaterial::set_density_texture);
+ ClassDB::bind_method(D_METHOD("get_density_texture"), &FogMaterial::get_density_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "density", PROPERTY_HINT_RANGE, "0.0,16.0,0.0001,or_greater,or_lesser"), "set_density", "get_density");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "albedo", PROPERTY_HINT_COLOR_NO_ALPHA), "set_albedo", "get_albedo");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height_falloff", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_height_falloff", "get_height_falloff");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_fade", PROPERTY_HINT_EXP_EASING), "set_edge_fade", "get_edge_fade");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "density_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_density_texture", "get_density_texture");
+}
+
+void FogMaterial::cleanup_shader() {
+ if (shader.is_valid()) {
+ RS::get_singleton()->free(shader);
+ }
+}
+
+void FogMaterial::_update_shader() {
+ shader_mutex.lock();
+ if (shader.is_null()) {
+ shader = RS::get_singleton()->shader_create();
+
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ RS::get_singleton()->shader_set_code(shader, R"(
+// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s FogMaterial.
+
+shader_type fog;
+
+uniform float density : hint_range(0, 1, 0.0001) = 1.0;
+uniform vec4 albedo : hint_color = vec4(1.0);
+uniform vec4 emission : hint_color = vec4(0, 0, 0, 1);
+uniform float height_falloff = 0.0;
+uniform float edge_fade = 0.1;
+uniform sampler3D density_texture: hint_white;
+
+
+void fog() {
+ DENSITY = density * clamp(exp2(-height_falloff * (WORLD_POSITION.y - OBJECT_POSITION.y)), 0.0, 1.0);
+ DENSITY *= texture(density_texture, UVW).r;
+ DENSITY *= pow(clamp(-SDF / min(min(EXTENTS.x, EXTENTS.y), EXTENTS.z), 0.0, 1.0), edge_fade);
+ ALBEDO = albedo.rgb;
+ EMISSION = emission.rgb;
+}
+)");
+ }
+ shader_mutex.unlock();
+}
+
+FogMaterial::FogMaterial() {
+ set_density(1.0);
+ set_albedo(Color(1, 1, 1, 1));
+ set_emission(Color(0, 0, 0, 1));
+
+ set_height_falloff(0.0);
+ set_edge_fade(0.1);
+}
+
+FogMaterial::~FogMaterial() {
+ RS::get_singleton()->material_set_shader(_get_material(), RID());
+}
diff --git a/scene/resources/fog_material.h b/scene/resources/fog_material.h
new file mode 100644
index 0000000000..e256bd4719
--- /dev/null
+++ b/scene/resources/fog_material.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* fog_material.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 FOG_MATERIAL_H
+#define FOG_MATERIAL_H
+
+#include "scene/resources/material.h"
+
+class FogMaterial : public Material {
+ GDCLASS(FogMaterial, Material);
+
+private:
+ float density = 1.0;
+ Color albedo = Color(1, 1, 1, 1);
+ Color emission = Color(0, 0, 0, 0);
+
+ float height_falloff = 0.0;
+
+ float edge_fade = 0.1;
+
+ Ref<Texture3D> density_texture;
+
+ static Mutex shader_mutex;
+ static RID shader;
+ static void _update_shader();
+ mutable bool shader_set = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_density(float p_density);
+ float get_density() const;
+
+ void set_albedo(Color p_color);
+ Color get_albedo() const;
+
+ void set_emission(Color p_color);
+ Color get_emission() const;
+
+ void set_height_falloff(float p_falloff);
+ float get_height_falloff() const;
+
+ void set_edge_fade(float p_edge_fade);
+ float get_edge_fade() const;
+
+ void set_density_texture(const Ref<Texture3D> &p_texture);
+ Ref<Texture3D> get_density_texture() const;
+
+ virtual Shader::Mode get_shader_mode() const override;
+ virtual RID get_shader_rid() const override;
+ virtual RID get_rid() const override;
+
+ static void cleanup_shader();
+
+ FogMaterial();
+ virtual ~FogMaterial();
+};
+
+#endif // FOG_MATERIAL_H
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 04e2b0dc70..d9de47afc7 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -70,6 +70,15 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
+ ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name);
+ ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name);
+
+ ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontData::set_font_style_name);
+ ClassDB::bind_method(D_METHOD("get_font_style_name"), &FontData::get_font_style_name);
+
+ ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontData::set_font_style);
+ ClassDB::bind_method(D_METHOD("get_font_style"), &FontData::get_font_style);
+
ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field);
ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field);
@@ -190,6 +199,15 @@ bool FontData::_set(const StringName &p_name, const Variant &p_value) {
} else if (tokens[0] == "antialiased") {
set_antialiased(p_value);
return true;
+ } else if (tokens[0] == "font_name") {
+ set_font_name(p_value);
+ return true;
+ } else if (tokens[0] == "style_name") {
+ set_font_style_name(p_value);
+ return true;
+ } else if (tokens[0] == "font_style") {
+ set_font_style(p_value);
+ return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
set_multichannel_signed_distance_field(p_value);
return true;
@@ -295,6 +313,15 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
} else if (tokens[0] == "antialiased") {
r_ret = is_antialiased();
return true;
+ } else if (tokens[0] == "font_name") {
+ r_ret = get_font_name();
+ return true;
+ } else if (tokens[0] == "style_name") {
+ r_ret = get_font_style_name();
+ return true;
+ } else if (tokens[0] == "font_style") {
+ r_ret = get_font_style();
+ return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
r_ret = is_multichannel_signed_distance_field();
return true;
@@ -394,6 +421,9 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
@@ -510,6 +540,36 @@ PackedByteArray FontData::get_data() const {
return data;
}
+void FontData::set_font_name(const String &p_name) {
+ _ensure_rid(0);
+ TS->font_set_name(cache[0], p_name);
+}
+
+String FontData::get_font_name() const {
+ _ensure_rid(0);
+ return TS->font_get_name(cache[0]);
+}
+
+void FontData::set_font_style_name(const String &p_name) {
+ _ensure_rid(0);
+ TS->font_set_style_name(cache[0], p_name);
+}
+
+String FontData::get_font_style_name() const {
+ _ensure_rid(0);
+ return TS->font_get_style_name(cache[0]);
+}
+
+void FontData::set_font_style(uint32_t p_style) {
+ _ensure_rid(0);
+ TS->font_set_style(cache[0], p_style);
+}
+
+uint32_t FontData::get_font_style() const {
+ _ensure_rid(0);
+ return TS->font_get_style(cache[0]);
+}
+
void FontData::set_antialiased(bool p_antialiased) {
if (antialiased != p_antialiased) {
antialiased = p_antialiased;
@@ -1072,10 +1132,6 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data);
ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data);
- ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size);
- ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size");
-
ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates);
ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates");
@@ -1087,20 +1143,20 @@ void Font::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM);
- ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE));
- ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
+ ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
@@ -1195,7 +1251,6 @@ void Font::reset_state() {
data.clear();
rids.clear();
- base_size = 16;
variation_coordinates.clear();
spacing_bottom = 0;
spacing_top = 0;
@@ -1308,14 +1363,6 @@ void Font::remove_data(int p_idx) {
notify_property_list_changed();
}
-void Font::set_base_size(int p_size) {
- base_size = p_size;
-}
-
-int Font::get_base_size() const {
- return base_size;
-}
-
void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) {
_data_changed();
variation_coordinates = p_variation_coordinates;
@@ -1355,51 +1402,46 @@ int Font::get_spacing(TextServer::SpacingType p_spacing) const {
}
real_t Font::get_height(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
+ ret = MAX(ret, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size));
}
return ret + spacing_bottom + spacing_top;
}
real_t Font::get_ascent(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_ascent(rids[i], size));
+ ret = MAX(ret, TS->font_get_ascent(rids[i], p_size));
}
return ret + spacing_top;
}
real_t Font::get_descent(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_descent(rids[i], size));
+ ret = MAX(ret, TS->font_get_descent(rids[i], p_size));
}
return ret + spacing_bottom;
}
real_t Font::get_underline_position(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_underline_position(rids[i], size));
+ ret = MAX(ret, TS->font_get_underline_position(rids[i], p_size));
}
return ret + spacing_top;
}
real_t Font::get_underline_thickness(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size));
+ ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_size));
}
return ret;
}
@@ -1407,8 +1449,6 @@ real_t Font::get_underline_thickness(int p_size) 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;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1418,14 +1458,14 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
hash = hash_djb2_one_64(p_flags, hash);
}
- hash = hash_djb2_one_64(size, hash);
+ hash = hash_djb2_one_64(p_size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
buffer.instantiate();
- buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
cache.insert(hash, buffer);
}
return buffer->get_size();
@@ -1434,8 +1474,6 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
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;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1443,14 +1481,14 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
- wrp_hash = hash_djb2_one_64(size, wrp_hash);
+ wrp_hash = hash_djb2_one_64(p_size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
lines_buffer.instantiate();
- lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
lines_buffer->set_width(p_width);
lines_buffer->set_flags(p_flags);
cache_wrap.insert(wrp_hash, lines_buffer);
@@ -1473,8 +1511,6 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
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;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1484,14 +1520,14 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
hash = hash_djb2_one_64(p_flags, hash);
}
- hash = hash_djb2_one_64(size, hash);
+ hash = hash_djb2_one_64(p_size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
buffer.instantiate();
- buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
cache.insert(hash, buffer);
}
@@ -1515,8 +1551,6 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
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;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1524,14 +1558,14 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
- wrp_hash = hash_djb2_one_64(size, wrp_hash);
+ wrp_hash = hash_djb2_one_64(p_size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
lines_buffer.instantiate();
- lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
lines_buffer->set_width(p_width);
lines_buffer->set_flags(p_flags);
cache_wrap.insert(wrp_hash, lines_buffer);
@@ -1573,16 +1607,14 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
}
Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
- Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0);
+ Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size));
if ((p_next != 0) && data[i]->has_char(p_next)) {
- int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
- ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0);
+ ret.x -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x;
}
return ret;
}
@@ -1591,22 +1623,20 @@ Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
}
real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
- real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x;
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0);
+ real_t ret = TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x;
if ((p_next != 0) && data[i]->has_char(p_next)) {
- int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
- ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0);
+ ret -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x;
}
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
- TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
+ TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
}
- TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate);
+ TS->font_draw_glyph(rids[i], p_canvas_item, p_size, p_pos, glyph_a, p_modulate);
return ret;
}
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index e65fdb0751..4d9ee72c84 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -79,6 +79,15 @@ public:
virtual PackedByteArray get_data() const;
// Common properties.
+ virtual void set_font_name(const String &p_name);
+ virtual String get_font_name() const;
+
+ virtual void set_font_style_name(const String &p_name);
+ virtual String get_font_style_name() const;
+
+ virtual void set_font_style(uint32_t p_style);
+ virtual uint32_t get_font_style() const;
+
virtual void set_antialiased(bool p_antialiased);
virtual bool is_antialiased() const;
@@ -219,7 +228,6 @@ class Font : public Resource {
mutable Vector<RID> rids;
// Font config.
- int base_size = 16;
Dictionary variation_coordinates;
int spacing_bottom = 0;
int spacing_top = 0;
@@ -237,6 +245,8 @@ protected:
virtual void reset_state() override;
public:
+ static const int DEFAULT_FONT_SIZE = 16;
+
Dictionary get_feature_list() const;
// Font data.
@@ -249,9 +259,6 @@ public:
virtual void remove_data(int p_idx);
// Font configuration.
- virtual void set_base_size(int p_size);
- virtual int get_base_size() const;
-
virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates);
virtual Dictionary get_variation_coordinates() const;
@@ -259,26 +266,26 @@ public:
virtual int get_spacing(TextServer::SpacingType p_spacing) const;
// Font metrics.
- virtual real_t get_height(int p_size = -1) const;
- virtual real_t get_ascent(int p_size = -1) const;
- virtual real_t get_descent(int p_size = -1) const;
- virtual real_t get_underline_position(int p_size = -1) const;
- virtual real_t get_underline_thickness(int p_size = -1) const;
+ virtual real_t get_height(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_ascent(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_descent(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_underline_position(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_underline_thickness(int p_size = DEFAULT_FONT_SIZE) 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, 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 Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, 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 = DEFAULT_FONT_SIZE, 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), 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;
+ 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 = DEFAULT_FONT_SIZE, 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 = DEFAULT_FONT_SIZE, 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;
virtual String get_supported_chars() const;
// Drawing char.
- virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
- virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, 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)) const;
+ virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
Vector<RID> get_rids() const;
diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp
index 7b9b942142..4559b4ce0a 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -51,6 +51,8 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "point", "offset"), &Gradient::set_offset);
ClassDB::bind_method(D_METHOD("get_offset", "point"), &Gradient::get_offset);
+ ClassDB::bind_method(D_METHOD("reverse"), &Gradient::reverse);
+
ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color);
ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color);
@@ -64,8 +66,18 @@ void Gradient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_colors", "colors"), &Gradient::set_colors);
ClassDB::bind_method(D_METHOD("get_colors"), &Gradient::get_colors);
+ ClassDB::bind_method(D_METHOD("set_interpolation_mode", "interpolation_mode"), &Gradient::set_interpolation_mode);
+ ClassDB::bind_method(D_METHOD("get_interpolation_mode"), &Gradient::get_interpolation_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode");
+
+ ADD_GROUP("Raw data", "");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
+
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_LINEAR);
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CONSTANT);
+ BIND_ENUM_CONSTANT(GRADIENT_INTERPOLATE_CUBIC);
}
Vector<float> Gradient::get_offsets() const {
@@ -86,6 +98,15 @@ Vector<Color> Gradient::get_colors() const {
return colors;
}
+void Gradient::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
+ interpolation_mode = p_interp_mode;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+Gradient::InterpolationMode Gradient::get_interpolation_mode() {
+ return interpolation_mode;
+}
+
void Gradient::set_offsets(const Vector<float> &p_offsets) {
points.resize(p_offsets.size());
for (int i = 0; i < points.size(); i++) {
@@ -127,6 +148,15 @@ void Gradient::remove_point(int p_index) {
emit_signal(CoreStringNames::get_singleton()->changed);
}
+void Gradient::reverse() {
+ for (int i = 0; i < points.size(); i++) {
+ points.write[i].offset = 1.0 - points[i].offset;
+ }
+
+ _update_sorting();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
void Gradient::set_points(Vector<Gradient::Point> &p_points) {
points = p_points;
is_sorted = false;
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index cf5b179c45..eb438d0bba 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -38,6 +38,12 @@ class Gradient : public Resource {
OBJ_SAVE_TYPE(Gradient);
public:
+ enum InterpolationMode {
+ GRADIENT_INTERPOLATE_LINEAR,
+ GRADIENT_INTERPOLATE_CONSTANT,
+ GRADIENT_INTERPOLATE_CUBIC,
+ };
+
struct Point {
float offset = 0.0;
Color color;
@@ -49,6 +55,8 @@ public:
private:
Vector<Point> points;
bool is_sorted = true;
+ InterpolationMode interpolation_mode = GRADIENT_INTERPOLATE_LINEAR;
+
_FORCE_INLINE_ void _update_sorting() {
if (!is_sorted) {
points.sort();
@@ -65,9 +73,9 @@ public:
void add_point(float p_offset, const Color &p_color);
void remove_point(int p_index);
-
void set_points(Vector<Point> &p_points);
Vector<Point> &get_points();
+ void reverse();
void set_offset(int pos, const float offset);
float get_offset(int pos);
@@ -81,6 +89,13 @@ public:
void set_colors(const Vector<Color> &p_colors);
Vector<Color> get_colors() const;
+ void set_interpolation_mode(InterpolationMode p_interp_mode);
+ InterpolationMode get_interpolation_mode();
+
+ _FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) {
+ return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0)));
+ }
+
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
if (points.is_empty()) {
return Color(0, 0, 0, 1);
@@ -88,7 +103,7 @@ public:
_update_sorting();
- //binary search
+ // Binary search.
int low = 0;
int high = points.size() - 1;
int middle = 0;
@@ -111,7 +126,7 @@ public:
}
}
- //return interpolated value
+ // Return interpolated value.
if (points[middle].offset > p_offset) {
middle--;
}
@@ -125,10 +140,44 @@ public:
}
const Point &pointFirst = points[first];
const Point &pointSecond = points[second];
- return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+
+ switch (interpolation_mode) {
+ case GRADIENT_INTERPOLATE_LINEAR: {
+ return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+ } break;
+ case GRADIENT_INTERPOLATE_CONSTANT: {
+ return pointFirst.color;
+ } break;
+ case GRADIENT_INTERPOLATE_CUBIC: {
+ int p0 = first - 1;
+ int p3 = second + 1;
+ if (p3 >= points.size()) {
+ p3 = second;
+ }
+ if (p0 < 0) {
+ p0 = first;
+ }
+ const Point &pointP0 = points[p0];
+ const Point &pointP3 = points[p3];
+
+ float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset);
+ float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
+ float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
+ float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
+ float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
+
+ return Color(r, g, b, a);
+ } break;
+ default: {
+ // Fallback to linear interpolation.
+ return pointFirst.color.lerp(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset));
+ }
+ }
}
int get_points_count() const;
};
+VARIANT_ENUM_CAST(Gradient::InterpolationMode);
+
#endif // GRADIENT_H
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index 2572c5de33..7afa4c91f0 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -997,7 +997,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
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 EditorSceneImporterMeshLightmapSurface {
+struct EditorSceneFormatImporterMeshLightmapSurface {
Ref<Material> material;
LocalVector<SurfaceTool::Vertex> vertices;
Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
@@ -1015,7 +1015,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform,
LocalVector<float> uv;
LocalVector<Pair<int, int>> uv_indices;
- Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
+ Vector<EditorSceneFormatImporterMeshLightmapSurface> lightmap_surfaces;
// Keep only the scale
Basis basis = p_base_transform.get_basis();
@@ -1027,7 +1027,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform,
Basis normal_basis = transform.basis.inverse().transposed();
for (int i = 0; i < get_surface_count(); i++) {
- EditorSceneImporterMeshLightmapSurface s;
+ 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.");
@@ -1243,5 +1243,5 @@ void ImporterMesh::_bind_methods() {
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");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
}
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index abb3381c4e..8399b14a56 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1086,7 +1086,7 @@ void BaseMaterial3D::_update_shader() {
code += " ALPHA = 1.0;\n";
} else if (transparency != TRANSPARENCY_DISABLED || flags[FLAG_USE_SHADOW_TO_OPACITY] || (distance_fade == DISTANCE_FADE_PIXEL_ALPHA) || proximity_fade_enabled) {
- code += " ALPHA = albedo.a * albedo_tex.a;\n";
+ code += " ALPHA *= albedo.a * albedo_tex.a;\n";
}
if (transparency == TRANSPARENCY_ALPHA_HASH) {
code += " ALPHA_HASH_SCALE = alpha_hash_scale;\n";
@@ -1100,7 +1100,7 @@ void BaseMaterial3D::_update_shader() {
if (proximity_fade_enabled) {
code += " float depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n";
- code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n";
+ code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex,1.0);\n";
code += " world_pos.xyz/=world_pos.w;\n";
code += " ALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n";
}
@@ -1695,7 +1695,7 @@ BaseMaterial3D::TextureFilter BaseMaterial3D::get_texture_filter() const {
void BaseMaterial3D::_validate_feature(const String &text, Feature feature, PropertyInfo &property) const {
if (property.name.begins_with(text) && property.name != text + "_enabled" && !features[feature]) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -1729,23 +1729,23 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
}
if (property.name == "billboard_keep_scale" && billboard_mode == BILLBOARD_DISABLED) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "grow_amount" && !grow_enabled) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "point_size" && !flags[FLAG_USE_POINT_SIZE]) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if (property.name == "proximity_fade_distance" && !proximity_fade_enabled) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
// you can only enable anti-aliasing (in materials) on alpha scissor and alpha hash
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 18e6a51118..51b4e1fbd8 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -1577,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;
@@ -1842,8 +1842,8 @@ void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_surfaces", "surfaces"), &ArrayMesh::_set_surfaces);
ClassDB::bind_method(D_METHOD("_get_surfaces"), &ArrayMesh::_get_surfaces);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_blend_shape_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_blend_shape_names", "_get_blend_shape_names");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_surfaces", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_surfaces", "_get_surfaces");
ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_shape_mode", PROPERTY_HINT_ENUM, "Normalized,Relative"), "set_blend_shape_mode", "get_blend_shape_mode");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shadow_mesh", PROPERTY_HINT_RESOURCE_TYPE, "ArrayMesh"), "set_shadow_mesh", "get_shadow_mesh");
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 8d5571d67c..a95b4d4a5e 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -278,7 +278,6 @@ public:
int surface_get_array_index_len(int p_idx) const override;
uint32_t surface_get_format(int p_idx) const override;
PrimitiveType surface_get_primitive_type(int p_idx) const override;
- bool surface_is_alpha_sorting_enabled(int p_idx) const;
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override;
virtual Ref<Material> surface_get_material(int p_idx) const override;
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index 04b2437ae8..9ecd8ec2f3 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -421,6 +421,7 @@ Vector<int> MeshDataTool::get_vertex_bones(int p_idx) const {
void MeshDataTool::set_vertex_bones(int p_idx, const Vector<int> &p_bones) {
ERR_FAIL_INDEX(p_idx, vertices.size());
+ ERR_FAIL_COND(p_bones.size() != 4);
vertices.write[p_idx].bones = p_bones;
format |= Mesh::ARRAY_FORMAT_BONES;
}
@@ -432,6 +433,7 @@ Vector<float> MeshDataTool::get_vertex_weights(int p_idx) const {
void MeshDataTool::set_vertex_weights(int p_idx, const Vector<float> &p_weights) {
ERR_FAIL_INDEX(p_idx, vertices.size());
+ ERR_FAIL_COND(p_weights.size() != 4);
vertices.write[p_idx].weights = p_weights;
format |= Mesh::ARRAY_FORMAT_WEIGHTS;
}
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index d87056f55d..db091ec37b 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -477,8 +477,8 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type");
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 60cda637ca..f43715a463 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -34,10 +34,12 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
+#include "editor/editor_inspector.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/node_3d.h"
#include "scene/gui/control.h"
#include "scene/main/instance_placeholder.h"
+#include "scene/property_utils.h"
#define PACKED_SCENE_VERSION 2
@@ -45,6 +47,30 @@ bool SceneState::can_instantiate() const {
return nodes.size() > 0;
}
+static Array _sanitize_node_pinned_properties(Node *p_node) {
+ if (!p_node->has_meta("_edit_pinned_properties_")) {
+ return Array();
+ }
+ Array pinned = p_node->get_meta("_edit_pinned_properties_");
+ if (pinned.is_empty()) {
+ return Array();
+ }
+ Set<StringName> storable_properties;
+ p_node->get_storable_properties(storable_properties);
+ int i = 0;
+ do {
+ if (storable_properties.has(pinned[i])) {
+ i++;
+ } else {
+ pinned.remove(i);
+ }
+ } while (i < pinned.size());
+ if (pinned.is_empty()) {
+ p_node->remove_meta("_edit_pinned_properties_");
+ }
+ return pinned;
+}
+
Node *SceneState::instantiate(GenEditState p_edit_state) const {
// nodes where instancing failed (because something is missing)
List<Node *> stray_instances;
@@ -227,7 +253,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
} else {
Node *base = i == 0 ? node : ret_nodes[0];
- if (p_edit_state == GEN_EDIT_STATE_MAIN) {
+ if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) {
//for the main scene, use the resource as is
res->configure_for_local_scene(base, resources_local_to_scene);
resources_local_to_scene[res] = res;
@@ -291,6 +317,13 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
+ // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting)
+ if (p_edit_state == GEN_EDIT_STATE_MAIN) {
+ _sanitize_node_pinned_properties(node);
+ } else {
+ node->remove_meta("_edit_pinned_properties_");
+ }
+
ret_nodes[i] = node;
if (node && gen_node_path_cache && ret_nodes[0]) {
@@ -415,61 +448,22 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// with the instance states, we can query for identical properties/groups
// and only save what has changed
- List<PackState> pack_state_stack;
-
- bool instantiated_by_owner = true;
-
- {
- Node *n = p_node;
-
- while (n) {
- if (n == p_owner) {
- Ref<SceneState> state = n->get_scene_inherited_state();
- if (state.is_valid()) {
- int node = state->find_node_by_path(n->get_path_to(p_node));
- if (node >= 0) {
- //this one has state for this node, save
- PackState ps;
- ps.node = node;
- ps.state = state;
- pack_state_stack.push_back(ps);
- instantiated_by_owner = false;
- }
- }
-
- 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_scene_file_path(), variant_map);
- nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
- } else {
- //must instance ourselves
- Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path());
- if (!instance.is_valid()) {
- return ERR_CANT_OPEN;
- }
+ bool instantiated_by_owner = false;
+ Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, p_owner, &instantiated_by_owner);
- nd.instance = _vm_get_variant(instance, variant_map);
- }
- }
- n = nullptr;
- } else {
- if (n->get_scene_file_path() != String()) {
- //is an instance
- Ref<SceneState> state = n->get_scene_instance_state();
- if (state.is_valid()) {
- int node = state->find_node_by_path(n->get_path_to(p_node));
- if (node >= 0) {
- //this one has state for this node, save
- PackState ps;
- ps.node = node;
- ps.state = state;
- pack_state_stack.push_back(ps);
- }
- }
- }
- n = n->get_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_scene_file_path(), variant_map);
+ nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
+ } else {
+ //must instance ourselves
+ Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path());
+ if (!instance.is_valid()) {
+ return ERR_CANT_OPEN;
}
+
+ nd.instance = _vm_get_variant(instance, variant_map);
}
}
@@ -478,88 +472,37 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
List<PropertyInfo> plist;
p_node->get_property_list(&plist);
- StringName type = p_node->get_class();
- Ref<Script> script = p_node->get_script();
- if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
- // Should be called in the editor only and not at runtime,
- // otherwise it can cause problems because of missing instance state support.
- script->update_exports();
- }
+ Array pinned_props = _sanitize_node_pinned_properties(p_node);
for (const PropertyInfo &E : plist) {
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- String name = E.name;
- Variant value = p_node->get(E.name);
-
- bool isdefault = false;
- Variant default_value = ClassDB::class_get_default_property_value(type, name);
-
- if (default_value.get_type() != Variant::NIL) {
- isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
- }
+ Variant forced_value;
- if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) {
- isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value));
- }
- // the version above makes more sense, because it does not rely on placeholder or usage flag
- // in the script, just the default value function.
- // if (E.usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) {
- // isdefault = true; //is script default value
- // }
-
- if (pack_state_stack.size()) {
- // we are on part of an instantiated subscene
- // or part of instantiated scene.
- // only save what has been changed
- // only save changed properties in instance
-
- if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E.name == "__meta__") {
- //property has requested that no instance state is saved, sorry
- //also, meta won't be overridden or saved
+ // If instance or inheriting, not saving if property requested so, or it's meta
+ if (states_stack.size()) {
+ if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) {
continue;
}
-
- bool exists = false;
- Variant original;
-
- for (List<PackState>::Element *F = pack_state_stack.back(); F; F = F->prev()) {
- //check all levels of pack to see if the property exists somewhere
- const PackState &ps = F->get();
-
- original = ps.state->get_property_value(ps.node, E.name, exists);
- if (exists) {
- break;
- }
- }
-
- if (exists) {
- //check if already exists and did not change
- if (value.get_type() == Variant::FLOAT && original.get_type() == Variant::FLOAT) {
- //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
- float a = value;
- float b = original;
-
- if (Math::is_equal_approx(a, b)) {
- continue;
- }
- } else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) {
- continue;
+ // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list
+ if (E.name == "__meta__") {
+ if (pinned_props.size()) {
+ Dictionary meta_override;
+ meta_override["_edit_pinned_properties_"] = pinned_props;
+ forced_value = meta_override;
}
}
+ }
- if (!exists && isdefault) {
- //does not exist in original node, but it's the default value
- //so safe to skip too.
- continue;
- }
+ StringName name = E.name;
+ Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value;
- } else {
- if (isdefault) {
- //it's the default value, no point in saving it
+ if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) {
+ Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &states_stack, true);
+ if (!PropertyUtils::is_property_value_different(value, default_value)) {
continue;
}
}
@@ -585,10 +528,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
*/
bool skip = false;
- for (const PackState &F : pack_state_stack) {
+ for (const SceneState::PackState &ia : states_stack) {
//check all levels of pack to see if the group was added somewhere
- const PackState &ps = F;
- if (ps.state->is_node_in_group(ps.node, gi.name)) {
+ if (ia.state->is_node_in_group(ia.node, gi.name)) {
skip = true;
break;
}
@@ -618,7 +560,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused
- if (pack_state_stack.is_empty() && !is_editable_instance) {
+ if (states_stack.is_empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type
nd.type = _nm_get_string(p_node->get_class(), name_map);
} else {
@@ -635,7 +577,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
save_node = save_node || p_node == p_owner; // owner is always saved
- save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated
+ save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instanced
int idx = nodes.size();
int parent_node = NO_PARENT_SAVED;
@@ -932,7 +874,7 @@ void SceneState::clear() {
base_scene_idx = -1;
}
-Ref<SceneState> SceneState::_get_base_scene_state() const {
+Ref<SceneState> SceneState::get_base_scene_state() const {
if (base_scene_idx >= 0) {
Ref<PackedScene> ps = variants[base_scene_idx];
if (ps.is_valid()) {
@@ -947,8 +889,8 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");
if (!node_path_cache.has(p_node)) {
- if (_get_base_scene_state().is_valid()) {
- int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ if (get_base_scene_state().is_valid()) {
+ int idx = get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
int rkey = _find_base_scene_node_remap_key(idx);
if (rkey == -1) {
@@ -963,11 +905,11 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
int nid = node_path_cache[p_node];
- if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
+ if (get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
//for nodes that _do_ exist in current scene, still try to look for
//the node in the instantiated scene, as a property may be missing
//from the local one
- int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ int idx = get_base_scene_state()->find_node_by_path(p_node);
if (idx != -1) {
base_scene_node_remap[nid] = idx;
}
@@ -1007,7 +949,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property,
//property not found, try on instance
if (base_scene_node_remap.has(p_node)) {
- return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found);
+ return get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found);
}
return Variant();
@@ -1026,7 +968,7 @@ bool SceneState::is_node_in_group(int p_node, const StringName &p_group) const {
}
if (base_scene_node_remap.has(p_node)) {
- return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group);
+ return get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group);
}
return false;
@@ -1065,7 +1007,7 @@ bool SceneState::is_connection(int p_node, const StringName &p_signal, int p_to_
}
if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) {
- return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method);
+ return get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method);
}
return false;
@@ -1488,7 +1430,7 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p
}
}
- ss = ss->_get_base_scene_state();
+ ss = ss->get_base_scene_state();
} while (ss.is_valid());
return false;
@@ -1610,6 +1552,7 @@ void SceneState::_bind_methods() {
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN);
+ BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED);
}
SceneState::SceneState() {
@@ -1651,7 +1594,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const {
s->set_scene_instance_state(state);
}
- if (get_path() != "" && get_path().find("::") == -1) {
+ if (!is_built_in()) {
s->set_scene_file_path(get_path());
}
@@ -1701,6 +1644,7 @@ void PackedScene::_bind_methods() {
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE);
BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN);
+ BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED);
}
PackedScene::PackedScene() {
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 55708f7914..a03da558e4 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -69,11 +69,6 @@ class SceneState : public RefCounted {
Vector<int> groups;
};
- struct PackState {
- Ref<SceneState> state;
- int node = -1;
- };
-
Vector<NodeData> nodes;
struct ConnectionData {
@@ -94,8 +89,6 @@ class SceneState : public RefCounted {
uint64_t last_modified_time = 0;
- _FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const;
-
static bool disable_placeholders;
Vector<String> _get_node_groups(int p_idx) const;
@@ -117,6 +110,12 @@ public:
GEN_EDIT_STATE_DISABLED,
GEN_EDIT_STATE_INSTANCE,
GEN_EDIT_STATE_MAIN,
+ GEN_EDIT_STATE_MAIN_INHERITED,
+ };
+
+ struct PackState {
+ Ref<SceneState> state;
+ int node = -1;
};
static void set_disable_placeholders(bool p_disable);
@@ -139,6 +138,8 @@ public:
bool can_instantiate() const;
Node *instantiate(GenEditState p_edit_state) const;
+ Ref<SceneState> get_base_scene_state() const;
+
//unbuild API
int get_node_count() const;
@@ -207,6 +208,7 @@ public:
GEN_EDIT_STATE_DISABLED,
GEN_EDIT_STATE_INSTANCE,
GEN_EDIT_STATE_MAIN,
+ GEN_EDIT_STATE_MAIN_INHERITED,
};
Error pack(Node *p_scene);
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index d9ec0bfd69..e77f5a0be7 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -1413,7 +1413,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
ADD_GROUP("Color", "");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture1D"), "set_color_ramp", "get_color_ramp");
ADD_GROUP("Hue Variation", "hue_");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION);
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index 4dd3c874cb..ec2022ed2f 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -556,7 +556,7 @@ void PolygonPathFinder::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data"), &PolygonPathFinder::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data);
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
}
PolygonPathFinder::PolygonPathFinder() {
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 77d915aef9..ea3b72af1b 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1492,7 +1492,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) {
} else {
if (internal_resources.has(res)) {
return "SubResource( \"" + internal_resources[res] + "\" )";
- } else if (res->get_path().length() && res->get_path().find("::") == -1) {
+ } else if (!res->is_built_in()) {
if (res->get_path() == local_path) { //circular reference attempt
return "null";
}
@@ -1515,7 +1515,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
return;
}
- if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) {
+ if (!p_main && (!bundle_resources) && !res->is_built_in()) {
if (res->get_path() == local_path) {
ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");
return;
@@ -1728,7 +1728,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) {
RES res = E->get();
- if (E->next() && (res->get_path() == "" || res->get_path().find("::") != -1)) {
+ if (E->next() && res->is_built_in()) {
if (res->get_scene_unique_id() != "") {
if (used_unique_ids.has(res->get_scene_unique_id())) {
res->set_scene_unique_id(""); // Repeated.
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 242e20f3b0..84fa07e4f4 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -49,6 +49,8 @@ void Shader::set_code(const String &p_code) {
mode = MODE_PARTICLES;
} else if (type == "sky") {
mode = MODE_SKY;
+ } else if (type == "fog") {
+ mode = MODE_FOG;
} else {
mode = MODE_SPATIAL;
}
@@ -95,28 +97,36 @@ RID Shader::get_rid() const {
return shader;
}
-void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture) {
+void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index) {
if (p_texture.is_valid()) {
- default_textures[p_param] = p_texture;
- RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid());
+ if (!default_textures.has(p_param)) {
+ default_textures[p_param] = Map<int, Ref<Texture2D>>();
+ }
+ default_textures[p_param][p_index] = p_texture;
+ RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid(), p_index);
} else {
- default_textures.erase(p_param);
- RS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID());
+ if (default_textures.has(p_param) && default_textures[p_param].has(p_index)) {
+ default_textures[p_param].erase(p_index);
+
+ if (default_textures[p_param].is_empty()) {
+ default_textures.erase(p_param);
+ }
+ }
+ RS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID(), p_index);
}
emit_changed();
}
-Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param) const {
- if (default_textures.has(p_param)) {
- return default_textures[p_param];
- } else {
- return Ref<Texture2D>();
+Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param, int p_index) const {
+ if (default_textures.has(p_param) && default_textures[p_param].has(p_index)) {
+ return default_textures[p_param][p_index];
}
+ return Ref<Texture2D>();
}
void Shader::get_default_texture_param_list(List<StringName> *r_textures) const {
- for (const KeyValue<StringName, Ref<Texture2D>> &E : default_textures) {
+ for (const KeyValue<StringName, Map<int, Ref<Texture2D>>> &E : default_textures) {
r_textures->push_back(E.key);
}
}
@@ -138,17 +148,18 @@ void Shader::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_code", "code"), &Shader::set_code);
ClassDB::bind_method(D_METHOD("get_code"), &Shader::get_code);
- ClassDB::bind_method(D_METHOD("set_default_texture_param", "param", "texture"), &Shader::set_default_texture_param);
- ClassDB::bind_method(D_METHOD("get_default_texture_param", "param"), &Shader::get_default_texture_param);
+ ClassDB::bind_method(D_METHOD("set_default_texture_param", "param", "texture", "index"), &Shader::set_default_texture_param, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_default_texture_param", "param", "index"), &Shader::get_default_texture_param, DEFVAL(0));
ClassDB::bind_method(D_METHOD("has_param", "name"), &Shader::has_param);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_code", "get_code");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_code", "get_code");
BIND_ENUM_CONSTANT(MODE_SPATIAL);
BIND_ENUM_CONSTANT(MODE_CANVAS_ITEM);
BIND_ENUM_CONSTANT(MODE_PARTICLES);
BIND_ENUM_CONSTANT(MODE_SKY);
+ BIND_ENUM_CONSTANT(MODE_FOG);
}
Shader::Shader() {
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 6563181ca2..c688dc1bab 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -46,6 +46,7 @@ public:
MODE_CANVAS_ITEM,
MODE_PARTICLES,
MODE_SKY,
+ MODE_FOG,
MODE_MAX
};
@@ -58,7 +59,7 @@ private:
// conversion fast and save memory.
mutable bool params_cache_dirty = true;
mutable Map<StringName, StringName> params_cache; //map a shader param to a material param..
- Map<StringName, Ref<Texture2D>> default_textures;
+ Map<StringName, Map<int, Ref<Texture2D>>> default_textures;
virtual void _update_shader() const; //used for visual shader
protected:
@@ -74,8 +75,8 @@ public:
void get_param_list(List<PropertyInfo> *p_params) const;
bool has_param(const StringName &p_param) const;
- void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_default_texture_param(const StringName &p_param) const;
+ void set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index = 0);
+ Ref<Texture2D> get_default_texture_param(const StringName &p_param, int p_index = 0) const;
void get_default_texture_param_list(List<StringName> *r_textures) const;
virtual bool is_text_shader() const;
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index 14bdd60e4b..7c5d1344e8 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -64,7 +64,6 @@ public:
static bool is_collision_outline_enabled();
- Shape2D();
~Shape2D();
};
diff --git a/scene/resources/skeleton_modification_2d_jiggle.cpp b/scene/resources/skeleton_modification_2d_jiggle.cpp
index 84abc9d020..31045455a3 100644
--- a/scene/resources/skeleton_modification_2d_jiggle.cpp
+++ b/scene/resources/skeleton_modification_2d_jiggle.cpp
@@ -194,9 +194,13 @@ void SkeletonModification2DJiggle::_execute_jiggle_joint(int p_joint_idx, Node2D
PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space());
PhysicsDirectSpaceState2D::RayResult ray_result;
+ PhysicsDirectSpaceState2D::RayParameters ray_params;
+ ray_params.from = operation_bone_trans.get_origin();
+ ray_params.to = jiggle_data_chain[p_joint_idx].dynamic_position;
+ ray_params.collision_mask = collision_mask;
+
// Add exception support?
- bool ray_hit = space_state->intersect_ray(operation_bone_trans.get_origin(), jiggle_data_chain[p_joint_idx].dynamic_position,
- ray_result, Set<RID>(), collision_mask);
+ bool ray_hit = space_state->intersect_ray(ray_params, ray_result);
if (ray_hit) {
jiggle_data_chain.write[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
diff --git a/scene/resources/skeleton_modification_2d_lookat.cpp b/scene/resources/skeleton_modification_2d_lookat.cpp
index 2da770f012..740937fc44 100644
--- a/scene/resources/skeleton_modification_2d_lookat.cpp
+++ b/scene/resources/skeleton_modification_2d_lookat.cpp
@@ -241,7 +241,7 @@ int SkeletonModification2DLookAt::get_bone_index() const {
void SkeletonModification2DLookAt::set_bone_index(int p_bone_idx) {
ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
- if (is_setup) {
+ if (is_setup && stack) {
if (stack->skeleton) {
ERR_FAIL_INDEX_MSG(p_bone_idx, stack->skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
bone_idx = p_bone_idx;
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
index e615615924..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 / (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 / (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..2535f2b987 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;
@@ -201,8 +206,12 @@ void SkeletonModification3DJiggle::_execute_jiggle_joint(int p_joint_idx, Node3D
Transform3D new_bone_trans_world = stack->skeleton->global_pose_to_world_transform(new_bone_trans);
Transform3D dynamic_position_world = stack->skeleton->global_pose_to_world_transform(Transform3D(Basis(), jiggle_data_chain[p_joint_idx].dynamic_position));
- bool ray_hit = space_state->intersect_ray(new_bone_trans_world.origin, dynamic_position_world.get_origin(),
- ray_result, Set<RID>(), collision_mask);
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = new_bone_trans_world.origin;
+ ray_params.to = dynamic_position_world.get_origin();
+ ray_params.collision_mask = collision_mask;
+
+ bool ray_hit = space_state->intersect_ray(ray_params, ray_result);
if (ray_hit) {
jiggle_data_chain[p_joint_idx].dynamic_position = jiggle_data_chain[p_joint_idx].last_noncollision_position;
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_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
index a724b732b9..c03210cf48 100644
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -125,6 +125,7 @@ Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_
}
void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) {
+ ERR_FAIL_NULL(p_mod);
p_mod->_setup_modification(this);
modifications.push_back(p_mod);
}
diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp
index 710612ae05..15cdb86bab 100644
--- a/scene/resources/skin.cpp
+++ b/scene/resources/skin.cpp
@@ -133,7 +133,7 @@ void Skin::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
for (int i = 0; i < get_bind_count(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bind/" + itos(i) + "/name"));
- p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose"));
}
}
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
index 01afb00283..5524d59dc7 100644
--- a/scene/resources/sprite_frames.cpp
+++ b/scene/resources/sprite_frames.cpp
@@ -233,7 +233,7 @@ void SpriteFrames::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations);
ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); //compatibility
}
SpriteFrames::SpriteFrames() {
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index 3381043d29..b960944d99 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -790,7 +790,7 @@ float StyleBoxFlat::get_style_margin(Side p_side) const {
void StyleBoxFlat::_validate_property(PropertyInfo &property) const {
if (!anti_aliased && property.name == "anti_aliasing_size") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index a8cd872408..455af8a40c 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -1174,9 +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_paragraph.cpp b/scene/resources/text_paragraph.cpp
index b2e18e2451..1b7fc64267 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -38,6 +38,11 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
+ ClassDB::bind_method(D_METHOD("set_custom_punctuation", "custom_punctuation"), &TextParagraph::set_custom_punctuation);
+ ClassDB::bind_method(D_METHOD("get_custom_punctuation"), &TextParagraph::get_custom_punctuation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_punctuation"), "set_custom_punctuation", "get_custom_punctuation");
+
ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
@@ -84,7 +89,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);
@@ -304,6 +309,15 @@ TextServer::Direction TextParagraph::get_direction() const {
return TS->shaped_text_get_direction(rid);
}
+void TextParagraph::set_custom_punctuation(const String &p_punct) {
+ TS->shaped_text_set_custom_punctuation(rid, p_punct);
+ lines_dirty = true;
+}
+
+String TextParagraph::get_custom_punctuation() const {
+ return TS->shaped_text_get_custom_punctuation(rid);
+}
+
void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
TS->shaped_text_set_orientation(rid, p_orientation);
TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
@@ -417,7 +431,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 69c50559df..4c4af43d14 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -96,6 +96,9 @@ public:
void set_bidi_override(const Array &p_override);
+ void set_custom_punctuation(const String &p_punct);
+ String get_custom_punctuation() const;
+
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();
@@ -120,7 +123,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;
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 1b3a7a5f2a..311bd9524b 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -32,6 +32,7 @@
#include "core/core_string_names.h"
#include "core/io/image_loader.h"
+#include "core/math/geometry_2d.h"
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
@@ -1502,6 +1503,7 @@ Ref<Curve> CurveTexture::get_curve() const {
}
void CurveTexture::set_texture_mode(TextureMode p_mode) {
+ ERR_FAIL_COND(p_mode < TEXTURE_MODE_RGB || p_mode > TEXTURE_MODE_RED);
if (texture_mode == p_mode) {
return;
}
@@ -1727,53 +1729,53 @@ CurveXYZTexture::~CurveXYZTexture() {
//////////////////
-GradientTexture::GradientTexture() {
+GradientTexture1D::GradientTexture1D() {
_queue_update();
}
-GradientTexture::~GradientTexture() {
+GradientTexture1D::~GradientTexture1D() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
}
-void GradientTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture::set_gradient);
- ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture::get_gradient);
+void GradientTexture1D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture1D::set_gradient);
+ ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture1D::get_gradient);
- ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture::set_width);
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture1D::set_width);
// The `get_width()` method is already exposed by the parent class Texture2D.
- ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture::set_use_hdr);
- ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture::is_using_hdr);
+ ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture1D::set_use_hdr);
+ ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture1D::is_using_hdr);
- ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update);
+ ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_update);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
}
-void GradientTexture::set_gradient(Ref<Gradient> p_gradient) {
+void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) {
if (p_gradient == gradient) {
return;
}
if (gradient.is_valid()) {
- gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture::_update));
+ gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update));
}
gradient = p_gradient;
if (gradient.is_valid()) {
- gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture::_update));
+ gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update));
}
_update();
emit_changed();
}
-Ref<Gradient> GradientTexture::get_gradient() const {
+Ref<Gradient> GradientTexture1D::get_gradient() const {
return gradient;
}
-void GradientTexture::_queue_update() {
+void GradientTexture1D::_queue_update() {
if (update_pending) {
return;
}
@@ -1782,7 +1784,7 @@ void GradientTexture::_queue_update() {
call_deferred(SNAME("_update"));
}
-void GradientTexture::_update() {
+void GradientTexture1D::_update() {
update_pending = false;
if (gradient.is_null()) {
@@ -1837,17 +1839,17 @@ void GradientTexture::_update() {
emit_changed();
}
-void GradientTexture::set_width(int p_width) {
+void GradientTexture1D::set_width(int p_width) {
ERR_FAIL_COND(p_width <= 0);
width = p_width;
_queue_update();
}
-int GradientTexture::get_width() const {
+int GradientTexture1D::get_width() const {
return width;
}
-void GradientTexture::set_use_hdr(bool p_enabled) {
+void GradientTexture1D::set_use_hdr(bool p_enabled) {
if (p_enabled == use_hdr) {
return;
}
@@ -1856,17 +1858,271 @@ void GradientTexture::set_use_hdr(bool p_enabled) {
_queue_update();
}
-bool GradientTexture::is_using_hdr() const {
+bool GradientTexture1D::is_using_hdr() const {
return use_hdr;
}
-Ref<Image> GradientTexture::get_image() const {
+Ref<Image> GradientTexture1D::get_image() const {
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
+GradientTexture2D::GradientTexture2D() {
+ _queue_update();
+}
+
+GradientTexture2D::~GradientTexture2D() {
+ if (texture.is_valid()) {
+ RS::get_singleton()->free(texture);
+ }
+}
+
+void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) {
+ if (gradient == p_gradient) {
+ return;
+ }
+ if (gradient.is_valid()) {
+ gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update));
+ }
+ gradient = p_gradient;
+ if (gradient.is_valid()) {
+ gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update));
+ }
+ _queue_update();
+}
+
+Ref<Gradient> GradientTexture2D::get_gradient() const {
+ return gradient;
+}
+
+void GradientTexture2D::_queue_update() {
+ if (update_pending) {
+ return;
+ }
+ update_pending = true;
+ call_deferred("_update");
+}
+
+void GradientTexture2D::_update() {
+ update_pending = false;
+
+ if (gradient.is_null()) {
+ return;
+ }
+ Ref<Image> image;
+ image.instantiate();
+
+ if (gradient->get_points_count() <= 1) { // No need to interpolate.
+ image->create(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8);
+ image->fill((gradient->get_points_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1));
+ } else {
+ if (use_hdr) {
+ image->create(width, height, false, Image::FORMAT_RGBAF);
+ Gradient &g = **gradient;
+ // `create()` isn't available for non-uint8_t data, so fill in the data manually.
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float ofs = _get_gradient_offset_at(x, y);
+ image->set_pixel(x, y, g.get_color_at_offset(ofs));
+ }
+ }
+ } else {
+ Vector<uint8_t> data;
+ data.resize(width * height * 4);
+ {
+ uint8_t *wd8 = data.ptrw();
+ Gradient &g = **gradient;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float ofs = _get_gradient_offset_at(x, y);
+ const Color &c = g.get_color_at_offset(ofs);
+
+ wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(c.r * 255.0, 0, 255));
+ wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(c.g * 255.0, 0, 255));
+ wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(c.b * 255.0, 0, 255));
+ wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(c.a * 255.0, 0, 255));
+ }
+ }
+ }
+ image->create(width, height, false, Image::FORMAT_RGBA8, data);
+ }
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ emit_changed();
+}
+
+float GradientTexture2D::_get_gradient_offset_at(int x, int y) const {
+ if (fill_to == fill_from) {
+ return 0;
+ }
+ float ofs = 0;
+ Vector2 pos;
+ if (width > 1) {
+ pos.x = static_cast<float>(x) / (width - 1);
+ }
+ if (height > 1) {
+ pos.y = static_cast<float>(y) / (height - 1);
+ }
+ if (fill == Fill::FILL_LINEAR) {
+ Vector2 segment[2];
+ segment[0] = fill_from;
+ segment[1] = fill_to;
+ Vector2 closest = Geometry2D::get_closest_point_to_segment_uncapped(pos, &segment[0]);
+ ofs = (closest - fill_from).length() / (fill_to - fill_from).length();
+ if ((closest - fill_from).dot(fill_to - fill_from) < 0) {
+ ofs *= -1;
+ }
+ } else if (fill == Fill::FILL_RADIAL) {
+ ofs = (pos - fill_from).length() / (fill_to - fill_from).length();
+ }
+ if (repeat == Repeat::REPEAT_NONE) {
+ ofs = CLAMP(ofs, 0.0, 1.0);
+ } else if (repeat == Repeat::REPEAT) {
+ ofs = Math::fmod(ofs, 1.0f);
+ if (ofs < 0) {
+ ofs = 1 + ofs;
+ }
+ } else if (repeat == Repeat::REPEAT_MIRROR) {
+ ofs = Math::abs(ofs);
+ ofs = Math::fmod(ofs, 2.0f);
+ if (ofs > 1.0) {
+ ofs = 2.0 - ofs;
+ }
+ }
+ return ofs;
+}
+
+void GradientTexture2D::set_width(int p_width) {
+ width = p_width;
+ _queue_update();
+}
+
+int GradientTexture2D::get_width() const {
+ return width;
+}
+
+void GradientTexture2D::set_height(int p_height) {
+ height = p_height;
+ _queue_update();
+}
+int GradientTexture2D::get_height() const {
+ return height;
+}
+
+void GradientTexture2D::set_use_hdr(bool p_enabled) {
+ if (p_enabled == use_hdr) {
+ return;
+ }
+
+ use_hdr = p_enabled;
+ _queue_update();
+}
+
+bool GradientTexture2D::is_using_hdr() const {
+ return use_hdr;
+}
+
+void GradientTexture2D::set_fill_from(Vector2 p_fill_from) {
+ fill_from = p_fill_from;
+ _queue_update();
+}
+
+Vector2 GradientTexture2D::get_fill_from() const {
+ return fill_from;
+}
+
+void GradientTexture2D::set_fill_to(Vector2 p_fill_to) {
+ fill_to = p_fill_to;
+ _queue_update();
+}
+
+Vector2 GradientTexture2D::get_fill_to() const {
+ return fill_to;
+}
+
+void GradientTexture2D::set_fill(Fill p_fill) {
+ fill = p_fill;
+ _queue_update();
+}
+
+GradientTexture2D::Fill GradientTexture2D::get_fill() const {
+ return fill;
+}
+
+void GradientTexture2D::set_repeat(Repeat p_repeat) {
+ repeat = p_repeat;
+ _queue_update();
+}
+
+GradientTexture2D::Repeat GradientTexture2D::get_repeat() const {
+ return repeat;
+}
+
+RID GradientTexture2D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+Ref<Image> GradientTexture2D::get_image() const {
+ if (!texture.is_valid()) {
+ return Ref<Image>();
+ }
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+}
+
+void GradientTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture2D::set_gradient);
+ ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture2D::get_gradient);
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture2D::set_width);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &GradientTexture2D::set_height);
+
+ ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture2D::set_use_hdr);
+ ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture2D::is_using_hdr);
+
+ ClassDB::bind_method(D_METHOD("set_fill", "fill"), &GradientTexture2D::set_fill);
+ ClassDB::bind_method(D_METHOD("get_fill"), &GradientTexture2D::get_fill);
+ ClassDB::bind_method(D_METHOD("set_fill_from", "fill_from"), &GradientTexture2D::set_fill_from);
+ ClassDB::bind_method(D_METHOD("get_fill_from"), &GradientTexture2D::get_fill_from);
+ ClassDB::bind_method(D_METHOD("set_fill_to", "fill_to"), &GradientTexture2D::set_fill_to);
+ ClassDB::bind_method(D_METHOD("get_fill_to"), &GradientTexture2D::get_fill_to);
+
+ ClassDB::bind_method(D_METHOD("set_repeat", "repeat"), &GradientTexture2D::set_repeat);
+ ClassDB::bind_method(D_METHOD("get_repeat"), &GradientTexture2D::get_repeat);
+
+ ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
+
+ ADD_GROUP("Fill", "fill_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill", PROPERTY_HINT_ENUM, "Linear,Radial"), "set_fill", "get_fill");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_from"), "set_fill_from", "get_fill_from");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_to"), "set_fill_to", "get_fill_to");
+
+ ADD_GROUP("Repeat", "repeat_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat", PROPERTY_HINT_ENUM, "No Repeat,Repeat,Mirror Repeat"), "set_repeat", "get_repeat");
+
+ BIND_ENUM_CONSTANT(FILL_LINEAR);
+ BIND_ENUM_CONSTANT(FILL_RADIAL);
+
+ BIND_ENUM_CONSTANT(REPEAT_NONE);
+ BIND_ENUM_CONSTANT(REPEAT);
+ BIND_ENUM_CONSTANT(REPEAT_MIRROR);
+}
+
//////////////////////////////////////
void ProxyTexture::_bind_methods() {
@@ -2615,15 +2871,6 @@ RID CameraTexture::get_rid() const {
}
}
-void CameraTexture::set_flags(uint32_t p_flags) {
- // not supported
-}
-
-uint32_t CameraTexture::get_flags() const {
- // not supported
- return 0;
-}
-
Ref<Image> CameraTexture::get_image() const {
// not (yet) supported
return Ref<Image>();
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 862b9a47a6..5b69711de6 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -669,8 +669,8 @@ public:
~CurveXYZTexture();
};
-class GradientTexture : public Texture2D {
- GDCLASS(GradientTexture, Texture2D);
+class GradientTexture1D : public Texture2D {
+ GDCLASS(GradientTexture1D, Texture2D);
public:
struct Point {
@@ -710,10 +710,81 @@ public:
virtual Ref<Image> get_image() const override;
- GradientTexture();
- virtual ~GradientTexture();
+ GradientTexture1D();
+ virtual ~GradientTexture1D();
};
+class GradientTexture2D : public Texture2D {
+ GDCLASS(GradientTexture2D, Texture2D);
+
+public:
+ enum Fill {
+ FILL_LINEAR,
+ FILL_RADIAL,
+ };
+ enum Repeat {
+ REPEAT_NONE,
+ REPEAT,
+ REPEAT_MIRROR,
+ };
+
+private:
+ Ref<Gradient> gradient;
+ mutable RID texture;
+
+ int width = 64;
+ int height = 64;
+
+ bool use_hdr = false;
+
+ Vector2 fill_from;
+ Vector2 fill_to = Vector2(1, 0);
+
+ Fill fill = FILL_LINEAR;
+ Repeat repeat = REPEAT_NONE;
+
+ float _get_gradient_offset_at(int x, int y) const;
+
+ bool update_pending = false;
+ void _queue_update();
+ void _update();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_gradient(Ref<Gradient> p_gradient);
+ Ref<Gradient> get_gradient() const;
+
+ void set_width(int p_width);
+ virtual int get_width() const override;
+ void set_height(int p_height);
+ virtual int get_height() const override;
+
+ void set_use_hdr(bool p_enabled);
+ bool is_using_hdr() const;
+
+ void set_fill(Fill p_fill);
+ Fill get_fill() const;
+ void set_fill_from(Vector2 p_fill_from);
+ Vector2 get_fill_from() const;
+ void set_fill_to(Vector2 p_fill_to);
+ Vector2 get_fill_to() const;
+
+ void set_repeat(Repeat p_repeat);
+ Repeat get_repeat() const;
+
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override { return true; }
+ virtual Ref<Image> get_image() const override;
+
+ GradientTexture2D();
+ virtual ~GradientTexture2D();
+};
+
+VARIANT_ENUM_CAST(GradientTexture2D::Fill);
+VARIANT_ENUM_CAST(GradientTexture2D::Repeat);
+
class ProxyTexture : public Texture2D {
GDCLASS(ProxyTexture, Texture2D);
@@ -829,9 +900,6 @@ public:
virtual RID get_rid() const override;
virtual bool has_alpha() const override;
- virtual void set_flags(uint32_t p_flags);
- virtual uint32_t get_flags() const;
-
virtual Ref<Image> get_image() const override;
void set_camera_feed_id(int p_new_id);
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 65cdc1e24e..99977a20f2 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -169,7 +169,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
const StringName *key2 = nullptr;
while ((key2 = font_size_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2));
+ list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2, PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
}
}
@@ -1655,7 +1655,7 @@ void Theme::_bind_methods() {
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");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater"), "set_default_font_size", "get_default_font_size");
BIND_ENUM_CONSTANT(DATA_TYPE_COLOR);
BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index f5e52b70e6..8c92445338 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,8 +40,275 @@
#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_NO_EDITOR | 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", "update_size"), &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 //////////////////////////////////////
+bool TileSet::TerrainsPattern::is_valid() const {
+ return valid;
+}
+
+bool TileSet::TerrainsPattern::is_erase_pattern() const {
+ return not_empty_terrains_count == 0;
+}
+
+bool TileSet::TerrainsPattern::operator<(const TerrainsPattern &p_terrains_pattern) const {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) {
+ return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i];
+ }
+ }
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
+ return bits[i] < p_terrains_pattern.bits[i];
+ }
+ }
+ return false;
+}
+
+bool TileSet::TerrainsPattern::operator==(const TerrainsPattern &p_terrains_pattern) const {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i] != p_terrains_pattern.is_valid_bit[i]) {
+ return false;
+ }
+ if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain) {
+ ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX);
+ ERR_FAIL_COND(!is_valid_bit[p_peering_bit]);
+ ERR_FAIL_COND(p_terrain < -1);
+
+ // Update the "is_erase_pattern" status.
+ if (p_terrain >= 0 && bits[p_peering_bit] < 0) {
+ not_empty_terrains_count++;
+ } else if (p_terrain < 0 && bits[p_peering_bit] >= 0) {
+ not_empty_terrains_count--;
+ }
+
+ bits[p_peering_bit] = p_terrain;
+}
+
+int TileSet::TerrainsPattern::get_terrain(TileSet::CellNeighbor p_peering_bit) const {
+ ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1);
+ ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1);
+ return bits[p_peering_bit];
+}
+
+void TileSet::TerrainsPattern::set_terrains_from_array(Array p_terrains) {
+ int in_array_index = 0;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i]) {
+ ERR_FAIL_COND(in_array_index >= p_terrains.size());
+ set_terrain(TileSet::CellNeighbor(i), p_terrains[in_array_index]);
+ in_array_index++;
+ }
+ }
+}
+
+Array TileSet::TerrainsPattern::get_terrains_as_array() const {
+ Array output;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (is_valid_bit[i]) {
+ output.push_back(bits[i]);
+ }
+ }
+ return output;
+}
+TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) {
+ ERR_FAIL_COND(p_terrain_set < 0);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ is_valid_bit[i] = (p_tile_set->is_valid_peering_bit_terrain(p_terrain_set, TileSet::CellNeighbor(i)));
+ bits[i] = -1;
+ }
+ valid = true;
+}
+
const int TileSet::INVALID_SOURCE = -1;
const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
@@ -117,6 +385,64 @@ int TileSet::get_next_source_id() const {
return next_source_id;
}
+void TileSet::_update_terrains_cache() {
+ if (terrains_cache_dirty) {
+ // Organizes tiles into structures.
+ per_terrain_pattern_tiles.resize(terrain_sets.size());
+ for (int i = 0; i < (int)per_terrain_pattern_tiles.size(); i++) {
+ per_terrain_pattern_tiles[i].clear();
+ }
+
+ for (const KeyValue<int, Ref<TileSetSource>> &kv : sources) {
+ Ref<TileSetSource> source = kv.value;
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
+ Vector2i tile_id = source->get_tile_id(tile_index);
+ for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
+ int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
+
+ // Executed for each tile_data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ int terrain_set = tile_data->get_terrain_set();
+ if (terrain_set >= 0) {
+ TileMapCell cell;
+ cell.source_id = kv.key;
+ cell.set_atlas_coords(tile_id);
+ cell.alternative_tile = alternative_id;
+
+ TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
+
+ // Terrain bits.
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ CellNeighbor bit = CellNeighbor(i);
+ if (is_valid_peering_bit_terrain(terrain_set, bit)) {
+ int terrain = terrains_pattern.get_terrain(bit);
+ if (terrain >= 0) {
+ per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Add the empty cell in the possible patterns and cells.
+ for (int i = 0; i < terrain_sets.size(); i++) {
+ TileSet::TerrainsPattern empty_pattern(this, i);
+
+ TileMapCell empty_cell;
+ empty_cell.source_id = TileSet::INVALID_SOURCE;
+ empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
+ per_terrain_pattern_tiles[i][empty_pattern].insert(empty_cell);
+ }
+ terrains_cache_dirty = false;
+ }
+}
+
void TileSet::_compute_next_source_id() {
while (sources.has(next_source_id)) {
next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30
@@ -137,6 +463,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source
sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
+ terrains_cache_dirty = true;
emit_changed();
return new_source_id;
@@ -152,6 +479,7 @@ void TileSet::remove_source(int p_source_id) {
source_ids.erase(p_source_id);
source_ids.sort();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -173,6 +501,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
_compute_next_source_id();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -361,6 +690,7 @@ void TileSet::add_terrain_set(int p_index) {
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -373,6 +703,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
source.value->move_terrain_set(p_from_index, p_to_pos);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -383,6 +714,7 @@ void TileSet::remove_terrain_set(int p_index) {
source.value->remove_terrain_set(p_index);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -394,6 +726,7 @@ void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -428,6 +761,7 @@ void TileSet::add_terrain(int p_terrain_set, int p_index) {
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -443,6 +777,7 @@ void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -456,6 +791,7 @@ void TileSet::remove_terrain(int p_terrain_set, int p_index) {
source.value->remove_terrain(p_terrain_set, p_index);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -982,6 +1318,103 @@ 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();
+}
+
+Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>());
+ _update_terrains_cache();
+
+ Set<TileSet::TerrainsPattern> output;
+ for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) {
+ output.insert(kv.key);
+ }
+ return output;
+}
+
+Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>());
+ _update_terrains_cache();
+ return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+}
+
+TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileMapCell());
+ _update_terrains_cache();
+
+ // Count the sum of probabilities.
+ double sum = 0.0;
+ Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+ for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ if (E->get().source_id >= 0) {
+ Ref<TileSetSource> source = sources[E->get().source_id];
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ sum += tile_data->get_probability();
+ } else {
+ sum += 1.0;
+ }
+ } else {
+ sum += 1.0;
+ }
+ }
+
+ // Generate a random number.
+ double count = 0.0;
+ double picked = Math::random(0.0, sum);
+
+ // Pick the tile.
+ for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ if (E->get().source_id >= 0) {
+ Ref<TileSetSource> source = sources[E->get().source_id];
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ count += tile_data->get_probability();
+ } else {
+ count += 1.0;
+ }
+ } else {
+ count += 1.0;
+ }
+
+ if (count >= picked) {
+ return E->get();
+ }
+ }
+
+ ERR_FAIL_V(TileMapCell());
+}
+
Vector<Vector2> TileSet::get_tile_shape_polygon() {
Vector<Vector2> points;
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
@@ -1296,6 +1729,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
}
void TileSet::_source_changed() {
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -2483,6 +2917,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
@@ -2606,6 +3046,13 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
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;
@@ -2677,15 +3124,20 @@ 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 (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));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
// Tile Proxies.
// Note: proxies need to be set after sources are set.
p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
- 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));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+
+ // 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_NO_EDITOR));
+ }
}
void TileSet::_validate_property(PropertyInfo &property) const {
@@ -2799,6 +3251,12 @@ 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_ARRAY("occlusion_layers", "occlusion_layer_");
@@ -2901,6 +3359,10 @@ void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) {
}
}
+const TileSet *TileSetAtlasSource::get_tile_set() const {
+ return tile_set;
+}
+
void TileSetAtlasSource::notify_tile_data_properties_should_change() {
// Set the TileSet on all TileData.
for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
@@ -3064,8 +3526,18 @@ void TileSetAtlasSource::reset_state() {
}
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
+ if (texture.is_valid()) {
+ texture->disconnect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
+ }
+
texture = p_texture;
+ if (texture.is_valid()) {
+ texture->connect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
+ }
+
+ _clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
@@ -3081,8 +3553,11 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
margins = p_margins;
}
+ _clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
+
Vector2i TileSetAtlasSource::get_margins() const {
return margins;
}
@@ -3095,8 +3570,11 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
separation = p_separation;
}
+ _clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
+
Vector2i TileSetAtlasSource::get_separation() const {
return separation;
}
@@ -3109,12 +3587,28 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
texture_region_size = p_tile_size;
}
+ _clear_tiles_outside_texture();
+ _queue_update_padded_texture();
emit_changed();
}
+
Vector2i TileSetAtlasSource::get_texture_region_size() const {
return texture_region_size;
}
+void TileSetAtlasSource::set_use_texture_padding(bool p_use_padding) {
+ if (use_texture_padding == p_use_padding) {
+ return;
+ }
+ use_texture_padding = p_use_padding;
+ _queue_update_padded_texture();
+ emit_changed();
+}
+
+bool TileSetAtlasSource::get_use_texture_padding() const {
+ return use_texture_padding;
+}
+
Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
Ref<Texture2D> texture = get_texture();
if (!texture.is_valid()) {
@@ -3275,35 +3769,35 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> tile_property_list;
// size_in_atlas
- property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
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);
+ property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.next_alternative_id == 1) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_columns.
- property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
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);
+ property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
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);
+ property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (E_tile.value.animation_speed == 1.0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
@@ -3315,7 +3809,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
// 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);
+ property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
if (!store_durations) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
@@ -3324,7 +3818,7 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
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_NO_EDITOR));
// Get the alternative tile's properties and append them to the list of properties.
List<PropertyInfo> alternative_property_list;
@@ -3354,7 +3848,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
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, tiles are already present in the space the tile would cover.");
+ 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;
@@ -3373,6 +3867,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
tiles_ids.sort();
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -3393,6 +3888,8 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
tiles_ids.erase(p_atlas_coords);
tiles_ids.sort();
+ _queue_update_padded_texture();
+
emit_signal(SNAME("changed"));
}
@@ -3421,6 +3918,7 @@ void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coord
tiles[p_atlas_coords].animation_columns = p_frame_columns;
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -3443,6 +3941,7 @@ void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_co
tiles[p_atlas_coords].animation_separation = p_separation;
_create_coords_mapping_cache(p_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
@@ -3470,19 +3969,24 @@ void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_
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);
+ int old_size = tiles[p_atlas_coords].animation_frames_durations.size();
+ if (p_frames_count == old_size) {
+ return;
+ }
+
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);
+ _queue_update_padded_texture();
notify_property_list_changed();
@@ -3552,9 +4056,7 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
return false;
}
if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) {
- if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) {
- return false; // Only accept tiles outside the atlas if they are part of the ignored tile.
- }
+ return false;
}
}
}
@@ -3562,6 +4064,37 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
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());
@@ -3592,6 +4125,31 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
return effective_texture_offset;
}
+// Getters for texture and tile region (padded or not)
+Ref<Texture2D> TileSetAtlasSource::get_runtime_texture() const {
+ if (use_texture_padding) {
+ return padded_texture;
+ } else {
+ return texture;
+ }
+}
+
+Rect2i TileSetAtlasSource::get_runtime_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());
+
+ Rect2i src_rect = get_tile_texture_region(p_atlas_coords, p_frame);
+ if (use_texture_padding) {
+ const TileAlternativesData &tad = tiles[p_atlas_coords];
+ Vector2i frame_coords = p_atlas_coords + (tad.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));
+ Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1);
+
+ return Rect2i(base_pos, src_rect.size);
+ } else {
+ return src_rect;
+ }
+}
+
void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
@@ -3622,38 +4180,11 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
tiles[new_atlas_coords].size_in_atlas = new_size;
_create_coords_mapping_cache(new_atlas_coords);
+ _queue_update_padded_texture();
emit_signal(SNAME("changed"));
}
-bool TileSetAtlasSource::has_tiles_outside_texture() {
- Vector2i grid_size = get_atlas_grid_size();
- Vector<Vector2i> to_remove;
-
- for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
- 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 (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
- 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));
@@ -3741,11 +4272,14 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation);
ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size);
ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size);
+ ClassDB::bind_method(D_METHOD("set_use_texture_padding", "use_texture_padding"), &TileSetAtlasSource::set_use_texture_padding);
+ ClassDB::bind_method(D_METHOD("get_use_texture_padding"), &TileSetAtlasSource::get_use_texture_padding);
- 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, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NO_EDITOR), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_margins", "get_margins");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_separation", "get_separation");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_texture_region_size", "get_texture_region_size");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_texture_padding", "get_use_texture_padding");
// Base tiles
ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
@@ -3754,7 +4288,7 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
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);
@@ -3779,9 +4313,12 @@ void TileSetAtlasSource::_bind_methods() {
// 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", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
+
+ // Getters for texture and tile region (padded or not)
+ ClassDB::bind_method(D_METHOD("_update_padded_texture"), &TileSetAtlasSource::_update_padded_texture);
+ ClassDB::bind_method(D_METHOD("get_runtime_texture"), &TileSetAtlasSource::get_runtime_texture);
+ ClassDB::bind_method(D_METHOD("get_runtime_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_runtime_tile_texture_region);
}
TileSetAtlasSource::~TileSetAtlasSource() {
@@ -3854,6 +4391,83 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i 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]);
+ }
+}
+
+void TileSetAtlasSource::_queue_update_padded_texture() {
+ padded_texture_needs_update = true;
+ call_deferred("_update_padded_texture");
+}
+
+void TileSetAtlasSource::_update_padded_texture() {
+ if (!padded_texture_needs_update) {
+ return;
+ }
+ padded_texture_needs_update = false;
+ padded_texture = Ref<ImageTexture>();
+
+ if (!texture.is_valid()) {
+ return;
+ }
+
+ if (!use_texture_padding) {
+ return;
+ }
+
+ Size2 size = get_atlas_grid_size() * (texture_region_size + Vector2i(2, 2));
+
+ Ref<Image> src = texture->get_image();
+
+ Ref<Image> image;
+ image.instantiate();
+ image->create(size.x, size.y, false, Image::FORMAT_RGBA8);
+
+ for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) {
+ for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) {
+ // Compute the source rects.
+ Rect2i src_rect = get_tile_texture_region(kv.key, frame);
+
+ Rect2i top_src_rect = Rect2i(src_rect.position, Vector2i(src_rect.size.x, 1));
+ Rect2i bottom_src_rect = Rect2i(src_rect.position + Vector2i(0, src_rect.size.y - 1), Vector2i(src_rect.size.x, 1));
+ Rect2i left_src_rect = Rect2i(src_rect.position, Vector2i(1, src_rect.size.y));
+ Rect2i right_src_rect = Rect2i(src_rect.position + Vector2i(src_rect.size.x - 1, 0), Vector2i(1, src_rect.size.y));
+
+ // Copy the tile and the paddings.
+ Vector2i frame_coords = kv.key + (kv.value.size_in_atlas + kv.value.animation_separation) * ((kv.value.animation_columns > 0) ? Vector2i(frame % kv.value.animation_columns, frame / kv.value.animation_columns) : Vector2i(frame, 0));
+ Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1);
+
+ image->blit_rect(*src, src_rect, base_pos);
+
+ image->blit_rect(*src, top_src_rect, base_pos + Vector2i(0, -1));
+ image->blit_rect(*src, bottom_src_rect, base_pos + Vector2i(0, src_rect.size.y));
+ image->blit_rect(*src, left_src_rect, base_pos + Vector2i(-1, 0));
+ image->blit_rect(*src, right_src_rect, base_pos + Vector2i(src_rect.size.x, 0));
+
+ image->set_pixelv(base_pos + Vector2i(-1, -1), src->get_pixelv(src_rect.position));
+ image->set_pixelv(base_pos + Vector2i(src_rect.size.x, -1), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, 0)));
+ image->set_pixelv(base_pos + Vector2i(-1, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(0, src_rect.size.y - 1)));
+ image->set_pixelv(base_pos + Vector2i(src_rect.size.x, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, src_rect.size.y - 1)));
+ }
+ }
+
+ if (!padded_texture.is_valid()) {
+ padded_texture.instantiate();
+ }
+ padded_texture->create_from_image(image);
+ emit_changed();
+}
+
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {
@@ -4245,6 +4859,37 @@ bool TileData::is_allowing_transform() const {
return allow_transform;
}
+TileData *TileData::duplicate() {
+ TileData *output = memnew(TileData);
+ output->tile_set = tile_set;
+
+ output->allow_transform = allow_transform;
+
+ // Rendering
+ output->flip_h = flip_h;
+ output->flip_v = flip_v;
+ output->transpose = transpose;
+ output->tex_offset = tex_offset;
+ output->material = material;
+ output->modulate = modulate;
+ output->z_index = z_index;
+ output->y_sort_origin = y_sort_origin;
+ output->occluders = occluders;
+ // Physics
+ output->physics = physics;
+ // Terrain
+ output->terrain_set = -1;
+ memcpy(output->terrain_peering_bits, terrain_peering_bits, 16 * sizeof(int));
+ // Navigation
+ output->navigation = navigation;
+ // Misc
+ output->probability = probability;
+ // Custom data
+ output->custom_data = custom_data;
+
+ return output;
+}
+
// Rendering
void TileData::set_flip_h(bool p_flip_h) {
ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
@@ -4352,6 +4997,9 @@ real_t TileData::get_constant_angular_velocity(int p_layer_id) const {
void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) {
ERR_FAIL_INDEX(p_layer_id, physics.size());
ERR_FAIL_COND(p_polygons_count < 0);
+ if (p_polygons_count == physics.write[p_layer_id].polygons.size()) {
+ return;
+ }
physics.write[p_layer_id].polygons.resize(p_polygons_count);
notify_property_list_changed();
emit_signal(SNAME("changed"));
@@ -4440,8 +5088,8 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i
Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const {
ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>());
- ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref<ConvexPolygonShape2D>());
- return physics[p_layer_id].polygons[shape_index].shapes[shape_index];
+ ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref<ConvexPolygonShape2D>());
+ return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index];
}
// Terrain
@@ -4488,6 +5136,18 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit)
return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
}
+TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
+ ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
+
+ TileSet::TerrainsPattern output(tile_set, terrain_set);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) {
+ output.set_terrain(TileSet::CellNeighbor(i), get_peering_bit_terrain(TileSet::CellNeighbor(i)));
+ }
+ }
+ return output;
+}
+
// Navigation
void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
ERR_FAIL_INDEX(p_layer_id, navigation.size());
@@ -4545,9 +5205,6 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(layer_index < 0, false);
if (components[1] == "polygon") {
Ref<OccluderPolygon2D> polygon = p_value;
- if (!polygon.is_valid()) {
- return false;
- }
if (layer_index >= occluders.size()) {
if (tile_set) {
@@ -4619,9 +5276,6 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(layer_index < 0, false);
if (components[1] == "polygon") {
Ref<NavigationPolygon> polygon = p_value;
- if (!polygon.is_valid()) {
- return false;
- }
if (layer_index >= navigation.size()) {
if (tile_set) {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 716b66405f..d2238c26d2 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);
@@ -176,6 +254,30 @@ public:
Vector2 offset;
};
+ class TerrainsPattern {
+ bool valid = false;
+ int bits[TileSet::CELL_NEIGHBOR_MAX];
+ bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
+
+ int not_empty_terrains_count = 0;
+
+ public:
+ bool is_valid() const;
+ bool is_erase_pattern() const;
+
+ bool operator<(const TerrainsPattern &p_terrains_pattern) const;
+ bool operator==(const TerrainsPattern &p_terrains_pattern) const;
+
+ void set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain);
+ int get_terrain(TileSet::CellNeighbor p_peering_bit) const;
+
+ void set_terrains_from_array(Array p_terrains);
+ Array get_terrains_as_array() const;
+
+ TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
+ TerrainsPattern() {}
+ };
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -225,6 +327,10 @@ private:
Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
bool terrain_bits_meshes_dirty = true;
+ LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
+ bool terrains_cache_dirty = true;
+ void _update_terrains_cache();
+
// Navigation
struct NavigationLayer {
uint32_t layers = 1;
@@ -245,6 +351,8 @@ private:
int next_source_id = 0;
// ---------------------
+ LocalVector<Ref<TileMapPattern>> patterns;
+
void _compute_next_source_id();
void _source_changed();
@@ -384,6 +492,17 @@ 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();
+
+ // Terrains.
+ Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
+ Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+ TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+
// Helpers
Vector<Vector2> get_tile_shape_polygon();
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
@@ -479,8 +598,16 @@ private:
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
- void _create_coords_mapping_cache(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();
+
+ bool use_texture_padding = true;
+ Ref<ImageTexture> padded_texture;
+ bool padded_texture_needs_update = false;
+ void _queue_update_padded_texture();
+ void _update_padded_texture();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -492,6 +619,7 @@ protected:
public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set) override;
+ const TileSet *get_tile_set() const;
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;
@@ -523,6 +651,10 @@ public:
void set_texture_region_size(Vector2i p_tile_size);
Vector2i get_texture_region_size() const;
+ // Padding.
+ void set_use_texture_padding(bool p_use_padding);
+ bool get_use_texture_padding() const;
+
// Base tiles.
void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1));
void remove_tile(Vector2i p_atlas_coords);
@@ -534,7 +666,7 @@ public:
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.
@@ -565,11 +697,13 @@ 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, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
+ // Getters for texture and tile region (padded or not)
+ Ref<Texture2D> get_runtime_texture() const;
+ Rect2i get_runtime_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
+
~TileSetAtlasSource();
};
@@ -647,7 +781,7 @@ private:
};
Vector2 linear_velocity;
- float angular_velocity = 0.0;
+ double angular_velocity = 0.0;
Vector<PolygonShapeTileData> polygons;
};
Vector<PhysicsLayerTileData> physics;
@@ -698,6 +832,9 @@ public:
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
+ // To duplicate a TileData object, needed for runtiume update.
+ TileData *duplicate();
+
// Rendering
void set_flip_h(bool p_flip_h);
bool get_flip_h() const;
@@ -745,6 +882,8 @@ public:
int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
+ TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
+
// Navigation
void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 934d16bd7e..0bdc81330e 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -36,6 +36,11 @@
#include "visual_shader_particle_nodes.h"
#include "visual_shader_sdf_nodes.h"
+String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
+ static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
+ return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
+}
+
bool VisualShaderNode::is_simple_decl() const {
return simple_decl;
}
@@ -113,6 +118,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;
@@ -195,6 +204,10 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const {
return Vector<StringName>();
}
+Map<StringName, String> VisualShaderNode::get_editable_properties_names() const {
+ return Map<StringName, String>();
+}
+
Array VisualShaderNode::get_default_input_values() const {
Array ret;
for (const KeyValue<int, Variant> &E : default_input_values) {
@@ -242,8 +255,8 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_default_input_values"), &VisualShaderNode::get_default_input_values);
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
ADD_SIGNAL(MethodInfo("editor_refresh_request"));
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
@@ -346,7 +359,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
}
String code = " {\n";
String _code;
- GDVIRTUAL_CALL(_get_code, input_vars, output_vars, (int)p_mode, (int)p_type, _code);
+ GDVIRTUAL_CALL(_get_code, input_vars, output_vars, p_mode, p_type, _code);
bool nend = _code.ends_with("\n");
_code = _code.insert(0, " ");
_code = _code.replace("\n", "\n ");
@@ -363,7 +376,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String ret;
- if (GDVIRTUAL_CALL(_get_global_code, (int)p_mode, ret)) {
+ if (GDVIRTUAL_CALL(_get_global_code, p_mode, ret)) {
String code = "// " + get_caption() + "\n";
code += ret;
code += "\n";
@@ -427,7 +440,7 @@ void VisualShaderNodeCustom::_bind_methods() {
ClassDB::bind_method(D_METHOD("_is_initialized"), &VisualShaderNodeCustom::_is_initialized);
ClassDB::bind_method(D_METHOD("_set_input_port_default_value", "port", "value"), &VisualShaderNodeCustom::_set_input_port_default_value);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "initialized", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_initialized", "_is_initialized");
}
VisualShaderNodeCustom::VisualShaderNodeCustom() {
@@ -1092,6 +1105,7 @@ static const char *type_string[VisualShader::TYPE_MAX] = {
"start_custom",
"process_custom",
"sky",
+ "fog",
};
bool VisualShader::_set(const StringName &p_name, const Variant &p_value) {
@@ -1241,7 +1255,7 @@ void VisualShader::reset_state() {
}
void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
//mode
- p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky"));
+ p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog"));
//render modes
Map<String, String> blend_mode_enums;
@@ -1286,20 +1300,20 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
prop_name += "/" + itos(E.key);
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::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
}
- p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
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));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
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));
+ p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
- p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
@@ -1632,7 +1646,7 @@ void VisualShader::_update_shader() const {
Vector<VisualShader::DefaultTextureParam> default_tex_params;
Set<StringName> classes;
Map<int, int> insertion_pos;
- static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky" };
+ static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog" };
global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n";
@@ -1680,7 +1694,7 @@ void VisualShader::_update_shader() const {
global_code += "render_mode " + render_mode + ";\n\n";
}
- static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky" };
+ static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" };
String global_expressions;
Set<String> used_uniform_names;
@@ -1751,7 +1765,7 @@ void VisualShader::_update_shader() const {
StringBuilder func_code;
bool is_empty_func = false;
- if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY) {
+ if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG) {
is_empty_func = true;
}
@@ -1820,6 +1834,7 @@ void VisualShader::_update_shader() const {
code += " vec3 __vec3_buff2;\n";
code += " float __scalar_buff1;\n";
code += " float __scalar_buff2;\n";
+ code += " int __scalar_ibuff;\n";
code += " vec3 __ndiff = normalize(__diff);\n\n";
}
if (has_start) {
@@ -1917,6 +1932,10 @@ void VisualShader::_update_shader() const {
global_compute_code += " return mat4(vec4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0), vec4(oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0), vec4(oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0), vec4(0, 0, 0, 1));\n";
global_compute_code += "}\n\n";
+ global_compute_code += "vec2 __get_random_unit_vec2(inout uint seed) {\n";
+ global_compute_code += " return normalize(vec2(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n";
+ global_compute_code += "}\n\n";
+
global_compute_code += "vec3 __get_random_unit_vec3(inout uint seed) {\n";
global_compute_code += " return normalize(vec3(__rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed), __rand_from_seed_m1_p1(seed)));\n";
global_compute_code += "}\n\n";
@@ -1943,7 +1962,9 @@ void VisualShader::_update_shader() const {
const_cast<VisualShader *>(this)->set_code(final_code);
for (int i = 0; i < default_tex_params.size(); i++) {
- const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param);
+ for (int j = 0; j < default_tex_params[i].params.size(); j++) {
+ const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].params[j], j);
+ }
}
if (previous_code != final_code) {
const_cast<VisualShader *>(this)->emit_signal(SNAME("changed"));
@@ -2012,8 +2033,8 @@ void VisualShader::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_engine_version", "get_engine_version");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_graph_offset", "get_graph_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_engine_version", "get_engine_version");
ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs.
@@ -2026,6 +2047,7 @@ void VisualShader::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_START_CUSTOM);
BIND_ENUM_CONSTANT(TYPE_PROCESS_CUSTOM);
BIND_ENUM_CONSTANT(TYPE_SKY);
+ BIND_ENUM_CONSTANT(TYPE_FOG);
BIND_ENUM_CONSTANT(TYPE_MAX);
BIND_CONSTANT(NODE_ID_INVALID);
@@ -2303,11 +2325,35 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Fog, Fog
+
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "world_position", "WORLD_POSITION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "object_position", "OBJECT_POSITION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "uvw", "UVW" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "extents", "EXTENTS" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "sdf", "SDF" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
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 +2361,63 @@ 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" },
// 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" },
+
+ // 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" },
+
+ // Fog
+
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2413,13 +2478,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;
}
}
@@ -2804,7 +2866,7 @@ void VisualShaderNodeUniformRef::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_uniform_type"), &VisualShaderNodeUniformRef::_get_uniform_type);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "uniform_name", PROPERTY_HINT_ENUM, ""), "set_uniform_name", "get_uniform_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "uniform_type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_uniform_type", "_get_uniform_type");
}
Vector<StringName> VisualShaderNodeUniformRef::get_editable_properties() const {
@@ -2903,6 +2965,13 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" },
////////////////////////////////////////////////////////////////////////
+ // Fog, Fog.
+ ////////////////////////////////////////////////////////////////////////
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
+
+ ////////////////////////////////////////////////////////////////////////
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -3383,7 +3452,7 @@ void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const Strin
count++;
}
- inputs.erase(index, count);
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs = inputs.insert(index, itos(i));
index += inputs_strings[i].size();
}
@@ -3406,7 +3475,7 @@ void VisualShaderNodeGroupBase::remove_input_port(int p_id) {
}
index += inputs_strings[i].size();
}
- inputs.erase(index, count);
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs_strings = inputs.split(";", false);
inputs = inputs.substr(0, index);
@@ -3459,7 +3528,7 @@ void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const Stri
count++;
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs = outputs.insert(index, itos(i));
index += outputs_strings[i].size();
}
@@ -3482,7 +3551,7 @@ void VisualShaderNodeGroupBase::remove_output_port(int p_id) {
}
index += outputs_strings[i].size();
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs_strings = outputs.split(";", false);
outputs = outputs.substr(0, index);
@@ -3534,8 +3603,7 @@ void VisualShaderNodeGroupBase::set_input_port_type(int p_id, int p_type) {
index += inputs_strings[i].size();
}
- inputs.erase(index, count);
-
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs = inputs.insert(index, itos(p_type));
_apply_port_changes();
@@ -3570,8 +3638,7 @@ void VisualShaderNodeGroupBase::set_input_port_name(int p_id, const String &p_na
index += inputs_strings[i].size();
}
- inputs.erase(index, count);
-
+ inputs = inputs.left(index) + inputs.substr(index + count);
inputs = inputs.insert(index, p_name);
_apply_port_changes();
@@ -3606,7 +3673,7 @@ void VisualShaderNodeGroupBase::set_output_port_type(int p_id, int p_type) {
index += output_strings[i].size();
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs = outputs.insert(index, itos(p_type));
@@ -3642,7 +3709,7 @@ void VisualShaderNodeGroupBase::set_output_port_name(int p_id, const String &p_n
index += output_strings[i].size();
}
- outputs.erase(index, count);
+ outputs = outputs.left(index) + outputs.substr(index + count);
outputs = outputs.insert(index, p_name);
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index b3efac02aa..5bb9d45f39 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -57,6 +57,7 @@ public:
TYPE_START_CUSTOM,
TYPE_PROCESS_CUSTOM,
TYPE_SKY,
+ TYPE_FOG,
TYPE_MAX
};
@@ -69,7 +70,7 @@ public:
struct DefaultTextureParam {
StringName name;
- Ref<Texture2D> param;
+ List<Ref<Texture2D>> params;
};
private:
@@ -254,6 +255,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;
@@ -269,6 +272,7 @@ public:
void set_disabled(bool p_disabled = true);
virtual Vector<StringName> get_editable_properties() const;
+ virtual Map<StringName, String> get_editable_properties_names() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
@@ -324,8 +328,8 @@ protected:
GDVIRTUAL0RC(int, _get_output_port_count)
GDVIRTUAL1RC(int, _get_output_port_type, int)
GDVIRTUAL1RC(String, _get_output_port_name, int)
- GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, int, int)
- GDVIRTUAL1RC(String, _get_global_code, int)
+ GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, Shader::Mode, VisualShader::Type)
+ GDVIRTUAL1RC(String, _get_global_code, Shader::Mode)
GDVIRTUAL0RC(bool, _is_highend)
protected:
@@ -693,4 +697,6 @@ public:
VisualShaderNodeGlobalExpression();
};
+extern String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name);
+
#endif // VISUAL_SHADER_H
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index e45dfdcb1b..951870fe34 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -494,15 +494,10 @@ String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
return "";
}
-static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) {
- static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" };
- return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id);
-}
-
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -900,7 +895,7 @@ String VisualShaderNodeCurveTexture::generate_code(Shader::Mode p_mode, VisualSh
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "curve");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -918,6 +913,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;
}
@@ -984,7 +980,7 @@ String VisualShaderNodeCurveXYZTexture::generate_code(Shader::Mode p_mode, Visua
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCurveXYZTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "curve3d");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1002,6 +998,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;
}
@@ -1165,7 +1162,7 @@ String VisualShaderNodeTexture2DArray::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture2DArray::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture_array;
+ dtp.params.push_back(texture_array);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1222,7 +1219,7 @@ String VisualShaderNodeTexture3D::get_input_port_name(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture3D::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "tex3d");
- dtp.param = texture;
+ dtp.params.push_back(texture);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
@@ -1321,7 +1318,7 @@ bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const {
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubemap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "cube");
- dtp.param = cube_map;
+ dtp.params.push_back(cube_map);
Vector<VisualShader::DefaultTextureParam> ret;
ret.push_back(dtp);
return ret;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 5fe801e037..7dd4eed15b 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -30,6 +30,8 @@
#include "visual_shader_particle_nodes.h"
+#include "core/core_string_names.h"
+
// VisualShaderNodeParticleEmitter
int VisualShaderNodeParticleEmitter::get_output_port_count() const {
@@ -47,6 +49,42 @@ String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
return String();
}
+bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const {
+ return false;
+}
+
+void VisualShaderNodeParticleEmitter::set_mode_2d(bool p_enabled) {
+ mode_2d = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleEmitter::is_mode_2d() const {
+ return mode_2d;
+}
+
+Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("mode_2d");
+ return props;
+}
+
+Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const {
+ Map<StringName, String> names;
+ names.insert("mode_2d", TTR("2D Mode"));
+ return names;
+}
+
+bool VisualShaderNodeParticleEmitter::is_show_prop_names() const {
+ return true;
+}
+
+void VisualShaderNodeParticleEmitter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mode_2d", "enabled"), &VisualShaderNodeParticleEmitter::set_mode_2d);
+ ClassDB::bind_method(D_METHOD("is_mode_2d"), &VisualShaderNodeParticleEmitter::is_mode_2d);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_2d"), "set_mode_2d", "is_mode_2d");
+}
+
VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
}
@@ -75,15 +113,27 @@ String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) co
String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
+
+ code += "vec2 __get_random_point_in_circle(inout uint seed, float radius, float inner_radius) {\n";
+ code += " return __get_random_unit_vec2(seed) * __randf_range(seed, inner_radius, radius);\n";
+ code += "}\n\n";
+
code += "vec3 __get_random_point_in_sphere(inout uint seed, float radius, float inner_radius) {\n";
code += " return __get_random_unit_vec3(seed) * __randf_range(seed, inner_radius, radius);\n";
code += "}\n\n";
+
return code;
}
String VisualShaderNodeParticleSphereEmitter::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 += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+
+ if (mode_2d) {
+ code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ } else {
+ code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
+ }
+
return code;
}
@@ -118,16 +168,27 @@ String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const
String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
- code += "vec3 __get_random_point_in_box(inout uint seed, vec3 extents) {\n";
+
+ code += "vec2 __get_random_point_in_box2d(inout uint seed, vec2 extents) {\n";
+ code += " vec2 half_extents = extents / 2.0;\n";
+ code += " return vec2(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y));\n";
+ code += "}\n\n";
+
+ code += "vec3 __get_random_point_in_box3d(inout uint seed, vec3 extents) {\n";
code += " vec3 half_extents = extents / 2.0;\n";
code += " return vec3(__randf_range(seed, -half_extents.x, half_extents.x), __randf_range(seed, -half_extents.y, half_extents.y), __randf_range(seed, -half_extents.z, half_extents.z));\n";
code += "}\n\n";
+
return code;
}
String VisualShaderNodeParticleBoxEmitter::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 += " " + p_output_vars[0] + " = __get_random_point_in_box(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
+ if (mode_2d) {
+ code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ".xy), 0.0);\n";
+ } else {
+ code += " " + p_output_vars[0] + " = __get_random_point_in_box3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
+ }
return code;
}
@@ -162,17 +223,31 @@ String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) cons
String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
- code += "vec3 __get_random_point_on_ring(inout uint seed, float radius, float inner_radius, float height) {\n";
- code += " float angle = __rand_from_seed(seed) * PI * 2.0;\n";
+
+ code += "vec2 __get_random_point_on_ring2d(inout uint seed, float radius, float inner_radius) {\n";
+ code += " float angle = __rand_from_seed(seed) * TAU;\n";
+ code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n";
+ code += " return vec2(ring.x, ring.y);\n";
+ code += "}\n\n";
+
+ code += "vec3 __get_random_point_on_ring3d(inout uint seed, float radius, float inner_radius, float height) {\n";
+ code += " float angle = __rand_from_seed(seed) * TAU;\n";
code += " vec2 ring = vec2(sin(angle), cos(angle)) * __randf_range(seed, inner_radius, radius);\n";
code += " return vec3(ring.x, __randf_range(seed, min(0.0, height), max(0.0, height)), ring.y);\n";
code += "}\n\n";
+
return code;
}
String VisualShaderNodeParticleRingEmitter::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 = " " + p_output_vars[0] + " = __get_random_point_on_ring(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
+
+ if (mode_2d) {
+ code = " " + p_output_vars[0] + " = vec3(__get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ } else {
+ code = " " + p_output_vars[0] + " = __get_random_point_on_ring3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
+ }
+
return code;
}
@@ -182,6 +257,283 @@ VisualShaderNodeParticleRingEmitter::VisualShaderNodeParticleRingEmitter() {
set_input_port_default_value(2, 0.0);
}
+// VisualShaderNodeParticleMeshEmitter
+
+String VisualShaderNodeParticleMeshEmitter::get_caption() const {
+ return "MeshEmitter";
+}
+
+int VisualShaderNodeParticleMeshEmitter::get_output_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_output_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR; // position
+ case 1:
+ return PORT_TYPE_VECTOR; // normal
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleMeshEmitter::get_output_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "position";
+ case 1:
+ return "normal";
+ }
+ return String();
+}
+
+int VisualShaderNodeParticleMeshEmitter::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeParticleMeshEmitter::get_input_port_name(int p_port) const {
+ return String();
+}
+
+String VisualShaderNodeParticleMeshEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code;
+
+ if (mesh.is_valid()) {
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n";
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n";
+ }
+
+ return code;
+}
+
+String VisualShaderNodeParticleMeshEmitter::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 += " __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n";
+
+ if (position_texture->get_width() == 0) {
+ code += " " + p_output_vars[0] + " = vec3(0.0);\n";
+ } else {
+ if (mode_2d) {
+ code += " " + p_output_vars[0] + " = vec3(";
+ code += "texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_vx") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n";
+ } else {
+ code += " " + p_output_vars[0] + " = texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_vx") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n";
+ }
+ }
+
+ if (normal_texture->get_width() == 0) {
+ code += " " + p_output_vars[1] + " = vec3(0.0);\n";
+ } else {
+ if (mode_2d) {
+ code += " " + p_output_vars[1] + " = vec3(";
+ code += "texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_nm") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n";
+ } else {
+ code += " " + p_output_vars[1] + " = texelFetch(";
+ code += make_unique_id(p_type, p_id, "mesh_nm") + ", ";
+ code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n";
+ }
+ }
+
+ return code;
+}
+
+Vector<VisualShader::DefaultTextureParam> VisualShaderNodeParticleMeshEmitter::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
+ VisualShader::DefaultTextureParam dtp_vx;
+ dtp_vx.name = make_unique_id(p_type, p_id, "mesh_vx");
+ dtp_vx.params.push_back(position_texture);
+
+ VisualShader::DefaultTextureParam dtp_nm;
+ dtp_nm.name = make_unique_id(p_type, p_id, "mesh_nm");
+ dtp_nm.params.push_back(normal_texture);
+
+ Vector<VisualShader::DefaultTextureParam> ret;
+ ret.push_back(dtp_vx);
+ ret.push_back(dtp_nm);
+ return ret;
+}
+
+void VisualShaderNodeParticleMeshEmitter::update_texture() {
+ if (!mesh.is_valid()) {
+ return;
+ }
+
+ Vector<Vector3> vertices;
+ Vector<Vector3> normals;
+
+ if (use_all_surfaces) {
+ for (int i = 0; i < max_surface_index; i++) {
+ Array vertex_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_VERTEX];
+ for (int j = 0; j < vertex_array.size(); j++) {
+ vertices.push_back((Vector3)vertex_array[j]);
+ }
+
+ Array normal_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_NORMAL];
+ for (int j = 0; j < vertex_array.size(); j++) {
+ normals.push_back((Vector3)vertex_array[j]);
+ }
+ }
+ } else {
+ Array vertex_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_VERTEX];
+ for (int i = 0; i < vertex_array.size(); i++) {
+ vertices.push_back((Vector3)vertex_array[i]);
+ }
+
+ Array normal_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_NORMAL];
+ for (int i = 0; i < normal_array.size(); i++) {
+ normals.push_back((Vector3)normal_array[i]);
+ }
+ }
+
+ // vertices
+ {
+ Ref<Image> image;
+ image.instantiate();
+ image->create(vertices.size(), 1, false, Image::Format::FORMAT_RGBF);
+
+ for (int i = 0; i < vertices.size(); i++) {
+ Vector3 v = vertices[i];
+ image->set_pixel(i, 0, Color(v.x, v.y, v.z));
+ }
+ if (position_texture->get_width() != vertices.size()) {
+ position_texture->create_from_image(image);
+ } else {
+ position_texture->update(image);
+ }
+ }
+
+ // normals
+ {
+ Ref<Image> image;
+ image.instantiate();
+ image->create(normals.size(), 1, false, Image::Format::FORMAT_RGBF);
+
+ for (int i = 0; i < normals.size(); i++) {
+ Vector3 v = normals[i];
+ image->set_pixel(i, 0, Color(v.x, v.y, v.z));
+ }
+ if (normal_texture->get_width() != normals.size()) {
+ normal_texture->create_from_image(image);
+ } else {
+ normal_texture->update(image);
+ }
+ }
+}
+
+void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref<Mesh> p_mesh) {
+ if (mesh == p_mesh) {
+ return;
+ }
+
+ if (p_mesh.is_valid()) {
+ max_surface_index = p_mesh->get_surface_count();
+ } else {
+ max_surface_index = 0;
+ }
+
+ if (mesh.is_valid()) {
+ Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture);
+
+ if (mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) {
+ mesh->disconnect(CoreStringNames::get_singleton()->changed, callable);
+ }
+ }
+
+ mesh = p_mesh;
+
+ if (mesh.is_valid()) {
+ Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture);
+
+ if (!mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) {
+ mesh->connect(CoreStringNames::get_singleton()->changed, callable);
+ }
+ }
+
+ emit_changed();
+}
+
+Ref<Mesh> VisualShaderNodeParticleMeshEmitter::get_mesh() const {
+ return mesh;
+}
+
+void VisualShaderNodeParticleMeshEmitter::set_use_all_surfaces(bool p_enabled) {
+ if (use_all_surfaces == p_enabled) {
+ return;
+ }
+ use_all_surfaces = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces() const {
+ return use_all_surfaces;
+}
+
+void VisualShaderNodeParticleMeshEmitter::set_surface_index(int p_surface_index) {
+ if (p_surface_index == surface_index || p_surface_index < 0 || p_surface_index >= max_surface_index) {
+ return;
+ }
+ surface_index = p_surface_index;
+ emit_changed();
+}
+
+int VisualShaderNodeParticleMeshEmitter::get_surface_index() const {
+ return surface_index;
+}
+
+Vector<StringName> VisualShaderNodeParticleMeshEmitter::get_editable_properties() const {
+ Vector<StringName> props = VisualShaderNodeParticleEmitter::get_editable_properties();
+
+ props.push_back("mesh");
+ props.push_back("use_all_surfaces");
+ if (!use_all_surfaces) {
+ props.push_back("surface_index");
+ }
+
+ return props;
+}
+
+Map<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const {
+ Map<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names();
+
+ names.insert("mesh", TTR("Mesh"));
+ names.insert("use_all_surfaces", TTR("Use All Surfaces"));
+ if (!use_all_surfaces) {
+ names.insert("surface_index", TTR("Surface Index"));
+ }
+
+ return names;
+}
+
+void VisualShaderNodeParticleMeshEmitter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &VisualShaderNodeParticleMeshEmitter::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &VisualShaderNodeParticleMeshEmitter::get_mesh);
+ ClassDB::bind_method(D_METHOD("set_use_all_surfaces", "enabled"), &VisualShaderNodeParticleMeshEmitter::set_use_all_surfaces);
+ ClassDB::bind_method(D_METHOD("is_use_all_surfaces"), &VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces);
+ ClassDB::bind_method(D_METHOD("set_surface_index", "surface_index"), &VisualShaderNodeParticleMeshEmitter::set_surface_index);
+ ClassDB::bind_method(D_METHOD("get_surface_index"), &VisualShaderNodeParticleMeshEmitter::get_surface_index);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_all_surfaces"), "set_use_all_surfaces", "is_use_all_surfaces");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "surface_index"), "set_surface_index", "get_surface_index");
+}
+
+VisualShaderNodeParticleMeshEmitter::VisualShaderNodeParticleMeshEmitter() {
+ connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture));
+
+ position_texture.instantiate();
+ normal_texture.instantiate();
+}
+
// VisualShaderNodeParticleMultiplyByAxisAngle
void VisualShaderNodeParticleMultiplyByAxisAngle::_bind_methods() {
@@ -261,10 +613,13 @@ bool VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode() const {
Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_properties() const {
Vector<StringName> props;
props.push_back("degrees_mode");
- props.push_back("axis_amount");
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 +668,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 +780,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 +884,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..0d0f21e4bf 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -38,10 +38,22 @@
class VisualShaderNodeParticleEmitter : public VisualShaderNode {
GDCLASS(VisualShaderNodeParticleEmitter, VisualShaderNode);
+protected:
+ bool mode_2d = false;
+ static void _bind_methods();
+
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;
+
+ void set_mode_2d(bool p_enabled);
+ bool is_mode_2d() const;
+
+ Vector<StringName> get_editable_properties() const override;
+ virtual Map<StringName, String> get_editable_properties_names() const override;
+ bool is_show_prop_names() const override;
VisualShaderNodeParticleEmitter();
};
@@ -94,6 +106,51 @@ public:
VisualShaderNodeParticleRingEmitter();
};
+class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitter {
+ GDCLASS(VisualShaderNodeParticleMeshEmitter, VisualShaderNodeParticleEmitter);
+ Ref<Mesh> mesh;
+ bool use_all_surfaces = true;
+ int surface_index = 0;
+ int max_surface_index = 0;
+
+ Ref<ImageTexture> position_texture;
+ Ref<ImageTexture> normal_texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ 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 int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) 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;
+
+ void update_texture();
+
+ void set_mesh(Ref<Mesh> p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ void set_use_all_surfaces(bool p_enabled);
+ bool is_use_all_surfaces() const;
+
+ void set_surface_index(int p_surface_index);
+ int get_surface_index() const;
+
+ Vector<StringName> get_editable_properties() const override;
+ Map<StringName, String> get_editable_properties_names() const override;
+ Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
+
+ VisualShaderNodeParticleMeshEmitter();
+};
+
class VisualShaderNodeParticleMultiplyByAxisAngle : public VisualShaderNode {
GDCLASS(VisualShaderNodeParticleMultiplyByAxisAngle, VisualShaderNode);
bool degrees_mode = true;
@@ -112,6 +169,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 +193,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 +227,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 +269,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_3d.cpp b/scene/resources/world_3d.cpp
index 42047f104f..0e1b343eac 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -144,9 +144,9 @@ World3D::World3D() {
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8));
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0)));
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1));
- ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"));
PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1));
- ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"));
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"));
navigation_map = NavigationServer3D::get_singleton()->map_create();
NavigationServer3D::get_singleton()->map_set_active(navigation_map, true);