diff options
Diffstat (limited to 'scene')
28 files changed, 1026 insertions, 364 deletions
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 530303f128..de648d404c 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -127,6 +127,7 @@ uint32_t CollisionObject2D::get_collision_mask() const { } void CollisionObject2D::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t collision_layer = get_collision_layer(); if (p_value) { collision_layer |= 1 << p_bit; @@ -137,10 +138,12 @@ void CollisionObject2D::set_collision_layer_bit(int p_bit, bool p_value) { } bool CollisionObject2D::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); return get_collision_layer() & (1 << p_bit); } void CollisionObject2D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -151,6 +154,7 @@ void CollisionObject2D::set_collision_mask_bit(int p_bit, bool p_value) { } bool CollisionObject2D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 50625a0f39..f6740040c1 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -55,6 +55,7 @@ uint32_t RayCast2D::get_collision_mask() const { } void RayCast2D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -65,6 +66,7 @@ void RayCast2D::set_collision_mask_bit(int p_bit, bool p_value) { } bool RayCast2D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 776d3bca5f..532a795b7c 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1250,6 +1250,7 @@ void TileMap::set_collision_mask(uint32_t p_mask) { } void TileMap::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t layer = get_collision_layer(); if (p_value) { layer |= 1 << p_bit; @@ -1260,6 +1261,7 @@ void TileMap::set_collision_layer_bit(int p_bit, bool p_value) { } void TileMap::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -1352,10 +1354,12 @@ uint32_t TileMap::get_collision_mask() const { } bool TileMap::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); return get_collision_layer() & (1 << p_bit); } bool TileMap::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } @@ -1522,6 +1526,12 @@ Vector2 TileMap::map_to_world(const Vector2 &p_pos, bool p_ignore_ofs) const { Vector2 TileMap::world_to_map(const Vector2 &p_pos) const { Vector2 ret = get_cell_transform().affine_inverse().xform(p_pos); + // Account for precision errors on the border (GH-23250). + // 0.00005 is 5*CMP_EPSILON, results would start being unpredictable if + // cell size is > 15,000, but we can hardly have more precision anyway with + // floating point. + ret += Vector2(0.00005, 0.00005); + switch (half_offset) { case HALF_OFFSET_X: { if (int(floor(ret.y)) & 1) { @@ -1548,11 +1558,6 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const { } } - // Account for precision errors on the border (GH-23250). - // 0.00005 is 5*CMP_EPSILON, results would start being unpredictable if - // cell size is > 15,000, but we can hardly have more precision anyway with - // floating point. - ret += Vector2(0.00005, 0.00005); return ret.floor(); } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index cd8d02233b..041da4f6ff 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -761,6 +761,7 @@ uint32_t ClippedCamera3D::get_collision_mask() const { } void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -771,6 +772,7 @@ void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool ClippedCamera3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 44de4eb449..688509a979 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -110,6 +110,7 @@ uint32_t CollisionObject3D::get_collision_mask() const { } void CollisionObject3D::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t collision_layer = get_collision_layer(); if (p_value) { collision_layer |= 1 << p_bit; @@ -120,10 +121,12 @@ void CollisionObject3D::set_collision_layer_bit(int p_bit, bool p_value) { } bool CollisionObject3D::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); return get_collision_layer() & (1 << p_bit); } void CollisionObject3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -134,6 +137,7 @@ void CollisionObject3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool CollisionObject3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index a075dcf990..5339b8a8da 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -181,12 +181,33 @@ void GPUParticles3D::set_draw_order(DrawOrder p_order) { RS::get_singleton()->particles_set_draw_order(particles, RS::ParticlesDrawOrder(p_order)); } +void GPUParticles3D::set_enable_trail(bool p_enabled) { + trail_enabled = p_enabled; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + update_configuration_warnings(); +} +void GPUParticles3D::set_trail_length(float p_seconds) { + ERR_FAIL_COND(p_seconds < 0.001); + trail_length = p_seconds; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); +} + +bool GPUParticles3D::is_trail_enabled() const { + return trail_enabled; +} +float GPUParticles3D::get_trail_length() const { + return trail_length; +} + GPUParticles3D::DrawOrder GPUParticles3D::get_draw_order() const { return draw_order; } void GPUParticles3D::set_draw_passes(int p_count) { ERR_FAIL_COND(p_count < 1); + for (int i = p_count; i < draw_passes.size(); i++) { + set_draw_pass_mesh(i, Ref<Mesh>()); + } draw_passes.resize(p_count); RS::get_singleton()->particles_set_draw_passes(particles, p_count); notify_property_list_changed(); @@ -199,8 +220,16 @@ int GPUParticles3D::get_draw_passes() const { void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { ERR_FAIL_INDEX(p_pass, draw_passes.size()); + if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { + draw_passes.write[p_pass]->disconnect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings)); + } + draw_passes.write[p_pass] = p_mesh; + if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { + draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), varray(), CONNECT_DEFERRED); + } + RID mesh_rid; if (p_mesh.is_valid()) { mesh_rid = p_mesh->get_rid(); @@ -208,6 +237,7 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { RS::get_singleton()->particles_set_draw_pass_mesh(particles, p_pass, mesh_rid); + _skinning_changed(); update_configuration_warnings(); } @@ -235,6 +265,15 @@ bool GPUParticles3D::get_fractional_delta() const { return fractional_delta; } +void GPUParticles3D::set_interpolate(bool p_enable) { + interpolate = p_enable; + RS::get_singleton()->particles_set_interpolate(particles, p_enable); +} + +bool GPUParticles3D::get_interpolate() const { + return interpolate; +} + TypedArray<String> GPUParticles3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); @@ -250,7 +289,7 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { meshes_found = true; for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) { anim_material_found = Object::cast_to<ShaderMaterial>(draw_passes[i]->surface_get_material(j).ptr()) != nullptr; - StandardMaterial3D *spat = Object::cast_to<StandardMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); } if (anim_material_found) { @@ -260,8 +299,10 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { } anim_material_found = anim_material_found || Object::cast_to<ShaderMaterial>(get_material_override().ptr()) != nullptr; - StandardMaterial3D *spat = Object::cast_to<StandardMaterial3D>(get_material_override().ptr()); - anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); + { + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(get_material_override().ptr()); + anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == BaseMaterial3D::BILLBOARD_PARTICLES); + } if (!meshes_found) { warnings.push_back(TTR("Nothing is visible because meshes have not been assigned to draw passes.")); @@ -274,7 +315,57 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const { if (!anim_material_found && process && (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { - warnings.push_back(TTR("Particles animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); + warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); + } + } + + if (trail_enabled) { + int dp_count = 0; + bool missing_trails = false; + bool no_materials = false; + + for (int i = 0; i < draw_passes.size(); i++) { + Ref<Mesh> draw_pass = draw_passes[i]; + if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { + dp_count++; + } + + if (draw_pass.is_valid()) { + int mats_found = 0; + for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) { + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); + if (spat) { + mats_found++; + } + if (spat && !spat->get_flag(BaseMaterial3D::FLAG_PARTICLE_TRAILS_MODE)) { + missing_trails = true; + } + } + + if (mats_found != draw_passes[i]->get_surface_count()) { + no_materials = true; + } + } + } + + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(get_material_override().ptr()); + if (spat) { + no_materials = false; + } + if (spat && !spat->get_flag(BaseMaterial3D::FLAG_PARTICLE_TRAILS_MODE)) { + missing_trails = true; + } + + if (dp_count && skin.is_valid()) { + warnings.push_back(TTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin.")); + } else if (dp_count == 0 && skin.is_null()) { + warnings.push_back(TTR("Trails active, but neither Trail meshes or a Skin were found.")); + } else if (dp_count > 1) { + warnings.push_back(TTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation).")); + } + + if ((dp_count || !skin.is_null()) && (missing_trails || no_materials)) { + warnings.push_back(TTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering.")); } } @@ -366,6 +457,47 @@ void GPUParticles3D::_notification(int p_what) { } } +void GPUParticles3D::_skinning_changed() { + Vector<Transform> xforms; + if (skin.is_valid()) { + xforms.resize(skin->get_bind_count()); + for (int i = 0; i < skin->get_bind_count(); i++) { + xforms.write[i] = skin->get_bind_pose(i); + } + } else { + for (int i = 0; i < draw_passes.size(); i++) { + Ref<Mesh> draw_pass = draw_passes[i]; + if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { + xforms.resize(draw_pass->get_builtin_bind_pose_count()); + for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) { + xforms.write[i] = draw_pass->get_builtin_bind_pose(j); + } + break; + } + } + } + + RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms); + update_configuration_warnings(); +} + +void GPUParticles3D::set_skin(const Ref<Skin> &p_skin) { + skin = p_skin; + _skinning_changed(); +} +Ref<Skin> GPUParticles3D::get_skin() const { + return skin; +} + +void GPUParticles3D::set_transform_align(TransformAlign p_align) { + ERR_FAIL_INDEX(uint32_t(p_align), 4); + transform_align = p_align; + RS::get_singleton()->particles_set_transform_align(particles, RS::ParticlesTransformAlign(transform_align)); +} +GPUParticles3D::TransformAlign GPUParticles3D::get_transform_align() const { + return transform_align; +} + void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles3D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles3D::set_amount); @@ -378,6 +510,7 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &GPUParticles3D::set_use_local_coordinates); ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &GPUParticles3D::set_fixed_fps); ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles3D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_interpolate", "enable"), &GPUParticles3D::set_interpolate); ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles3D::set_process_material); ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles3D::set_speed_scale); ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles3D::set_collision_base_size); @@ -393,6 +526,7 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &GPUParticles3D::get_use_local_coordinates); ClassDB::bind_method(D_METHOD("get_fixed_fps"), &GPUParticles3D::get_fixed_fps); ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles3D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_interpolate"), &GPUParticles3D::get_interpolate); ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles3D::get_process_material); ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles3D::get_speed_scale); ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles3D::get_collision_base_size); @@ -407,6 +541,9 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_draw_passes"), &GPUParticles3D::get_draw_passes); ClassDB::bind_method(D_METHOD("get_draw_pass_mesh", "pass"), &GPUParticles3D::get_draw_pass_mesh); + ClassDB::bind_method(D_METHOD("set_skin", "skin"), &GPUParticles3D::set_skin); + ClassDB::bind_method(D_METHOD("get_skin"), &GPUParticles3D::get_skin); + ClassDB::bind_method(D_METHOD("restart"), &GPUParticles3D::restart); ClassDB::bind_method(D_METHOD("capture_aabb"), &GPUParticles3D::capture_aabb); @@ -415,6 +552,15 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle); + ClassDB::bind_method(D_METHOD("set_enable_trail", "enabled"), &GPUParticles3D::set_enable_trail); + ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles3D::set_trail_length); + + ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles3D::is_trail_enabled); + ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles3D::get_trail_length); + + ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align); + ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter"); @@ -426,6 +572,7 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size"); @@ -433,6 +580,10 @@ void GPUParticles3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,ZBillboard,YToVelocity,ZBillboardYToVelocity"), "set_transform_align", "get_transform_align"); + ADD_GROUP("Trails", "trail_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_enable_trail", "is_trail_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,4,0.01"), "set_trail_length", "get_trail_length"); ADD_GROUP("Process Material", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Draw Passes", "draw_"); @@ -440,6 +591,7 @@ void GPUParticles3D::_bind_methods() { for (int i = 0; i < MAX_DRAW_PASSES; i++) { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "draw_pass_" + itos(i + 1), PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_draw_pass_mesh", "get_draw_pass_mesh", i); } + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "draw_skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); @@ -452,6 +604,11 @@ void GPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM); BIND_CONSTANT(MAX_DRAW_PASSES); + + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_DISABLED); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY); } GPUParticles3D::GPUParticles3D() { @@ -462,17 +619,20 @@ GPUParticles3D::GPUParticles3D() { set_one_shot(false); set_amount(8); set_lifetime(1); - set_fixed_fps(0); + set_fixed_fps(30); set_fractional_delta(true); + set_interpolate(true); set_pre_process_time(0); set_explosiveness_ratio(0); set_randomness_ratio(0); + set_trail_length(0.3); set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))); set_use_local_coordinates(true); set_draw_passes(1); set_draw_order(DRAW_ORDER_INDEX); set_speed_scale(1); set_collision_base_size(0.01); + set_transform_align(TRANSFORM_ALIGN_DISABLED); } GPUParticles3D::~GPUParticles3D() { diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index b9e2b5ccef..1f9cea79b6 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -34,6 +34,7 @@ #include "core/templates/rid.h" #include "scene/3d/visual_instance_3d.h" #include "scene/resources/material.h" +#include "scene/resources/skin.h" class GPUParticles3D : public GeometryInstance3D { private: @@ -46,6 +47,13 @@ public: DRAW_ORDER_VIEW_DEPTH, }; + enum TransformAlign { + TRANSFORM_ALIGN_DISABLED, + TRANSFORM_ALIGN_Z_BILLBOARD, + TRANSFORM_ALIGN_Y_TO_VELOCITY, + TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY + }; + enum { MAX_DRAW_PASSES = 4 }; @@ -64,17 +72,26 @@ private: bool local_coords; int fixed_fps; bool fractional_delta; + bool interpolate = true; NodePath sub_emitter; float collision_base_size; + bool trail_enabled = false; + float trail_length = 0.3; + + TransformAlign transform_align = TRANSFORM_ALIGN_DISABLED; + Ref<Material> process_material; DrawOrder draw_order; Vector<Ref<Mesh>> draw_passes; + Ref<Skin> skin; void _attach_sub_emitter(); + void _skinning_changed(); + protected: static void _bind_methods(); void _notification(int p_what); @@ -96,6 +113,8 @@ public: void set_process_material(const Ref<Material> &p_material); void set_speed_scale(float p_scale); void set_collision_base_size(float p_ratio); + void set_enable_trail(bool p_enabled); + void set_trail_length(float p_seconds); bool is_emitting() const; int get_amount() const; @@ -109,6 +128,8 @@ public: Ref<Material> get_process_material() const; float get_speed_scale() const; float get_collision_base_size() const; + bool is_trail_enabled() const; + float get_trail_length() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; @@ -116,6 +137,9 @@ public: void set_fractional_delta(bool p_enable); bool get_fractional_delta() const; + void set_interpolate(bool p_enable); + bool get_interpolate() const; + void set_draw_order(DrawOrder p_order); DrawOrder get_draw_order() const; @@ -130,6 +154,12 @@ public: void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; + void set_skin(const Ref<Skin> &p_skin); + Ref<Skin> get_skin() const; + + void set_transform_align(TransformAlign p_align); + TransformAlign get_transform_align() const; + void restart(); enum EmitFlags { @@ -148,6 +178,7 @@ public: }; VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder) +VARIANT_ENUM_CAST(GPUParticles3D::TransformAlign) VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags) #endif // PARTICLES_H diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 66f3e539a2..95638ce514 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -61,6 +61,7 @@ uint32_t RayCast3D::get_collision_mask() const { } void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -71,6 +72,7 @@ void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool RayCast3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 9a7984b06a..dc4deb0570 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -496,6 +496,7 @@ uint32_t SoftBody3D::get_collision_layer() const { } void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -506,10 +507,12 @@ void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { } bool SoftBody3D::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); uint32_t layer = get_collision_layer(); if (p_value) { layer |= 1 << p_bit; @@ -520,6 +523,7 @@ void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { } bool SoftBody3D::get_collision_layer_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); return get_collision_layer() & (1 << p_bit); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index f569fbc420..191f94b2b8 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2688,15 +2688,15 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List if (p_idx == 0) { List<StringName> sn; String pf = p_function; - if (pf == "add_color_override" || pf == "has_color" || pf == "has_color_override" || pf == "get_color") { + if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { Theme::get_default()->get_color_list(get_class(), &sn); - } else if (pf == "add_style_override" || pf == "has_style" || pf == "has_style_override" || pf == "get_style") { + } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { Theme::get_default()->get_stylebox_list(get_class(), &sn); - } else if (pf == "add_font_override" || pf == "has_font" || pf == "has_font_override" || pf == "get_font") { + } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { Theme::get_default()->get_font_list(get_class(), &sn); - } else if (pf == "add_font_size_override" || pf == "has_font_size" || pf == "has_font_size_override" || pf == "get_font_size") { + } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { Theme::get_default()->get_font_size_list(get_class(), &sn); - } else if (pf == "add_constant_override" || pf == "has_constant" || pf == "has_constant_override" || pf == "get_constant") { + } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { Theme::get_default()->get_constant_list(get_class(), &sn); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 4f508423b3..0713df9955 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2054,6 +2054,7 @@ void TextEdit::indent_selected_lines_left() { if (is_selection_active() && get_selection_to_column() == 0) { end_line--; } + String first_line_text = get_line(start_line); String last_line_text = get_line(end_line); for (int i = start_line; i <= end_line; i++) { @@ -2078,10 +2079,17 @@ void TextEdit::indent_selected_lines_left() { } } - // Fix selection and cursor being off by one on the last line. - if (is_selection_active() && last_line_text != get_line(end_line)) { - select(selection.from_line, selection.from_column - removed_characters, - selection.to_line, initial_selection_end_column - removed_characters); + if (is_selection_active()) { + // Fix selection being off by one on the first line. + if (first_line_text != get_line(start_line)) { + select(selection.from_line, selection.from_column - removed_characters, + selection.to_line, initial_selection_end_column); + } + // Fix selection being off by one on the last line. + if (last_line_text != get_line(end_line)) { + select(selection.from_line, selection.from_column, + selection.to_line, initial_selection_end_column - removed_characters); + } } cursor_set_column(initial_cursor_column - removed_characters, false); end_complex_operation(); diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 08ab71e7fa..884696d58d 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -40,9 +40,7 @@ Error HTTPRequest::_request() { } Error HTTPRequest::_parse_url(const String &p_url) { - url = p_url; use_ssl = false; - request_string = ""; port = 80; request_sent = false; @@ -52,35 +50,20 @@ Error HTTPRequest::_parse_url(const String &p_url) { downloaded.set(0); redirections = 0; - String url_lower = url.to_lower(); - if (url_lower.begins_with("http://")) { - url = url.substr(7, url.length() - 7); - } else if (url_lower.begins_with("https://")) { - url = url.substr(8, url.length() - 8); + String scheme; + Error err = p_url.parse_url(scheme, url, port, request_string); + ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing URL: " + p_url + "."); + if (scheme == "https://") { use_ssl = true; - port = 443; - } else { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Malformed URL: " + url + "."); + } else if (scheme != "http://") { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid URL scheme: " + scheme + "."); } - - ERR_FAIL_COND_V_MSG(url.length() < 1, ERR_INVALID_PARAMETER, "URL too short: " + url + "."); - - int slash_pos = url.find("/"); - - if (slash_pos != -1) { - request_string = url.substr(slash_pos, url.length()); - url = url.substr(0, slash_pos); - } else { - request_string = "/"; + if (port == 0) { + port = use_ssl ? 443 : 80; } - - int colon_pos = url.find(":"); - if (colon_pos != -1) { - port = url.substr(colon_pos + 1, url.length()).to_int(); - url = url.substr(0, colon_pos); - ERR_FAIL_COND_V(port < 1 || port > 65535, ERR_INVALID_PARAMETER); + if (request_string.is_empty()) { + request_string = "/"; } - return OK; } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index b7313749d6..c90d3e4a32 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -591,7 +591,7 @@ Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallEr if (p_args[0]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -620,7 +620,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal if (p_args[1]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -643,7 +643,7 @@ Variant Node::_rpc_unreliable_bind(const Variant **p_args, int p_argcount, Calla if (p_args[0]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } @@ -672,7 +672,7 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Ca if (p_args[1]->get_type() != Variant::STRING_NAME) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; - r_error.expected = Variant::STRING; + r_error.expected = Variant::STRING_NAME; return Variant(); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f1613f2fe5..f861e3064c 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1412,6 +1412,16 @@ void Viewport::_update_canvas_items(Node *p_node) { } } +void Viewport::set_use_xr(bool p_use_xr) { + use_xr = p_use_xr; + + RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); +} + +bool Viewport::is_using_xr() { + return use_xr; +} + Ref<ViewportTexture> Viewport::get_texture() const { return default_texture; } @@ -1830,6 +1840,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = nullptr; Point2 mpos = mb->get_position(); + gui.last_mouse_pos = mpos; if (mb->is_pressed()) { Size2 pos = mpos; if (gui.mouse_focus_mask) { @@ -3498,6 +3509,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("get_render_info", "info"), &Viewport::get_render_info); + ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr); + ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr); + ClassDB::bind_method(D_METHOD("get_texture"), &Viewport::get_texture); ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking); @@ -3578,6 +3592,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d"); @@ -3746,16 +3761,6 @@ Viewport::~Viewport() { ///////////////////////////////// -void SubViewport::set_use_xr(bool p_use_xr) { - xr = p_use_xr; - - RS::get_singleton()->viewport_set_use_xr(get_viewport_rid(), xr); -} - -bool SubViewport::is_using_xr() { - return xr; -} - void SubViewport::set_size(const Size2i &p_size) { _set_size(p_size, _get_size_2d_override(), Rect2i(), _stretch_transform(), true); } @@ -3828,9 +3833,6 @@ void SubViewport::_notification(int p_what) { } void SubViewport::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &SubViewport::set_use_xr); - ClassDB::bind_method(D_METHOD("is_using_xr"), &SubViewport::is_using_xr); - ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size); ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size); @@ -3846,7 +3848,6 @@ void SubViewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode); ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "xr"), "set_use_xr", "is_using_xr"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_2d_override"), "set_size_2d_override", "get_size_2d_override"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled"); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 6786b70a6b..2e88e1251d 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -234,6 +234,7 @@ private: Size2i size; Size2i size_2d_override; bool size_allocated = false; + bool use_xr = false; RID contact_2d_debug; RID contact_3d_debug_multimesh; @@ -534,6 +535,9 @@ public: void set_transparent_background(bool p_enable); bool has_transparent_background() const; + void set_use_xr(bool p_use_xr); + bool is_using_xr(); + Ref<ViewportTexture> get_texture() const; void set_shadow_atlas_size(int p_size); @@ -656,7 +660,6 @@ public: private: UpdateMode update_mode = UPDATE_WHEN_VISIBLE; ClearMode clear_mode = CLEAR_MODE_ALWAYS; - bool xr = false; bool size_2d_override_stretch = false; protected: @@ -672,9 +675,6 @@ public: void set_size_2d_override(const Size2i &p_size); Size2i get_size_2d_override() const; - void set_use_xr(bool p_use_xr); - bool is_using_xr(); - void set_size_2d_override_stretch(bool p_enable); bool is_size_2d_override_stretch_enabled() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 1b3be13039..b16532676f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -689,6 +689,8 @@ void register_scene_types() { ClassDB::register_class<PrismMesh>(); ClassDB::register_class<QuadMesh>(); ClassDB::register_class<SphereMesh>(); + ClassDB::register_class<TubeTrailMesh>(); + ClassDB::register_class<RibbonTrailMesh>(); ClassDB::register_class<PointMesh>(); ClassDB::register_virtual_class<Material>(); ClassDB::register_virtual_class<BaseMaterial3D>(); @@ -729,7 +731,6 @@ void register_scene_types() { ClassDB::register_class<ImageTexture>(); ClassDB::register_class<AtlasTexture>(); ClassDB::register_class<MeshTexture>(); - ClassDB::register_class<LargeTexture>(); ClassDB::register_class<CurveTexture>(); ClassDB::register_class<GradientTexture>(); ClassDB::register_class<ProxyTexture>(); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index bc479e557a..846da39221 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -445,10 +445,10 @@ void Curve::set_bake_resolution(int p_resolution) { _baked_cache_dirty = true; } -real_t Curve::interpolate_baked(real_t offset) { +real_t Curve::interpolate_baked(real_t offset) const { if (_baked_cache_dirty) { // Last-second bake if not done already - bake(); + const_cast<Curve *>(this)->bake(); } // Special cases if the cache is too small diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 402c893cd8..746c6fa597 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -122,7 +122,7 @@ public: void bake(); int get_bake_resolution() const { return _bake_resolution; } void set_bake_resolution(int p_resolution); - real_t interpolate_baked(real_t offset); + real_t interpolate_baked(real_t offset) const; void ensure_default_setup(float p_min, float p_max); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index d5a018ef41..e8157c7165 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -543,6 +543,9 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_DISABLE_DEPTH_TEST]) { code += ",depth_test_disabled"; } + if (flags[FLAG_PARTICLE_TRAILS_MODE]) { + code += ",particle_trails"; + } if (shading_mode == SHADING_MODE_PER_VERTEX) { code += ",vertex_lighting"; } @@ -1597,6 +1600,9 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) { if (p_flag == FLAG_USE_SHADOW_TO_OPACITY || p_flag == FLAG_USE_TEXTURE_REPEAT || p_flag == FLAG_SUBSURFACE_MODE_SKIN || p_flag == FLAG_USE_POINT_SIZE) { notify_property_list_changed(); } + if (p_flag == FLAG_PARTICLE_TRAILS_MODE) { + update_configuration_warning(); + } _queue_shader_change(); } @@ -2177,6 +2183,8 @@ Shader::Mode BaseMaterial3D::get_shader_mode() const { } void BaseMaterial3D::_bind_methods() { + static_assert(sizeof(MaterialKey) == 16, "MaterialKey should be 16 bytes"); + ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &BaseMaterial3D::set_albedo); ClassDB::bind_method(D_METHOD("get_albedo"), &BaseMaterial3D::get_albedo); @@ -2534,6 +2542,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_point_size"), "set_flag", "get_flag", FLAG_USE_POINT_SIZE); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_point_size", "get_point_size"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_particle_trails"), "set_flag", "get_flag", FLAG_PARTICLE_TRAILS_MODE); ADD_GROUP("Proximity Fade", "proximity_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance"); @@ -2635,6 +2644,7 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_USE_TEXTURE_REPEAT); BIND_ENUM_CONSTANT(FLAG_INVERT_HEIGHTMAP); BIND_ENUM_CONSTANT(FLAG_SUBSURFACE_MODE_SKIN); + BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); diff --git a/scene/resources/material.h b/scene/resources/material.h index 70452a5f74..ad1b7b3e33 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -235,6 +235,7 @@ public: FLAG_USE_TEXTURE_REPEAT, FLAG_INVERT_HEIGHTMAP, FLAG_SUBSURFACE_MODE_SKIN, + FLAG_PARTICLE_TRAILS_MODE, FLAG_MAX }; @@ -305,16 +306,15 @@ private: uint64_t roughness_channel : get_num_bits(TEXTURE_CHANNEL_MAX - 1); uint64_t emission_op : get_num_bits(EMISSION_OP_MAX - 1); uint64_t distance_fade : get_num_bits(DISTANCE_FADE_MAX - 1); - - // flag bitfield - uint64_t feature_mask : FEATURE_MAX - 1; - uint64_t flags : FLAG_MAX - 1; - // booleans uint64_t deep_parallax : 1; uint64_t grow : 1; uint64_t proximity_fade : 1; + // flag bitfield + uint32_t feature_mask; + uint32_t flags; + MaterialKey() { memset(this, 0, sizeof(MaterialKey)); } diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index f8e1ce6a61..33ad15b938 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -579,6 +579,13 @@ Vector<Ref<Shape3D>> Mesh::convex_decompose() const { return ret; } +int Mesh::get_builtin_bind_pose_count() const { + return 0; +} +Transform Mesh::get_builtin_bind_pose(int p_index) const { + return Transform(); +} + Mesh::Mesh() { } @@ -1394,7 +1401,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, 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, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache); +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; struct ArrayMeshLightmapSurface { Ref<Material> material; @@ -1404,28 +1411,28 @@ struct ArrayMeshLightmapSurface { }; Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) { - int *cache_data = nullptr; - unsigned int cache_size = 0; - bool use_cache = false; // Don't use cache - return lightmap_unwrap_cached(cache_data, cache_size, use_cache, p_base_transform, p_texel_size); + Vector<uint8_t> null_cache; + return lightmap_unwrap_cached(p_base_transform, p_texel_size, null_cache, null_cache, false); } -Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) { +Error ArrayMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); - Vector<float> vertices; - Vector<float> normals; - Vector<int> indices; - Vector<float> uv; - Vector<Pair<int, int>> uv_indices; + LocalVector<float> vertices; + LocalVector<float> normals; + LocalVector<int> indices; + LocalVector<float> uv; + LocalVector<Pair<int, int>> uv_indices; Vector<ArrayMeshLightmapSurface> lightmap_surfaces; // Keep only the scale - Transform transform = p_base_transform; - transform.origin = Vector3(); - transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0)); + Basis basis = p_base_transform.get_basis(); + Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + + Transform transform; + transform.scale(scale); Basis normal_basis = transform.basis.inverse().transposed(); @@ -1439,14 +1446,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach Array arrays = surface_get_arrays(i); s.material = surface_get_material(i); - SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices); + SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format); - Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX]; + PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX]; int vc = rvertices.size(); - const Vector3 *r = rvertices.ptr(); - Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL]; - const Vector3 *rn = rnormals.ptr(); + PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL]; int vertex_ofs = vertices.size() / 3; @@ -1455,24 +1460,29 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach uv_indices.resize(vertex_ofs + vc); for (int j = 0; j < vc; j++) { - Vector3 v = transform.xform(r[j]); - Vector3 n = normal_basis.xform(rn[j]).normalized(); - - vertices.write[(j + vertex_ofs) * 3 + 0] = v.x; - vertices.write[(j + vertex_ofs) * 3 + 1] = v.y; - vertices.write[(j + vertex_ofs) * 3 + 2] = v.z; - normals.write[(j + vertex_ofs) * 3 + 0] = n.x; - normals.write[(j + vertex_ofs) * 3 + 1] = n.y; - normals.write[(j + vertex_ofs) * 3 + 2] = n.z; - uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j); + Vector3 v = transform.xform(rvertices[j]); + Vector3 n = normal_basis.xform(rnormals[j]).normalized(); + + vertices[(j + vertex_ofs) * 3 + 0] = v.x; + vertices[(j + vertex_ofs) * 3 + 1] = v.y; + vertices[(j + vertex_ofs) * 3 + 2] = v.z; + normals[(j + vertex_ofs) * 3 + 0] = n.x; + normals[(j + vertex_ofs) * 3 + 1] = n.y; + normals[(j + vertex_ofs) * 3 + 2] = n.z; + uv_indices[j + vertex_ofs] = Pair<int, int>(i, j); } - Vector<int> rindices = arrays[Mesh::ARRAY_INDEX]; + PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX]; int ic = rindices.size(); + float eps = 1.19209290e-7F; // Taken from xatlas.h if (ic == 0) { for (int j = 0; j < vc / 3; j++) { - if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) { + Vector3 p0 = transform.xform(rvertices[j * 3 + 0]); + Vector3 p1 = transform.xform(rvertices[j * 3 + 1]); + Vector3 p2 = transform.xform(rvertices[j * 3 + 2]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { continue; } @@ -1482,15 +1492,18 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach } } else { - const int *ri = rindices.ptr(); - for (int j = 0; j < ic / 3; j++) { - if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) { + Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]); + Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]); + Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]); + + if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) { continue; } - indices.push_back(vertex_ofs + ri[j * 3 + 0]); - indices.push_back(vertex_ofs + ri[j * 3 + 1]); - indices.push_back(vertex_ofs + ri[j * 3 + 2]); + + indices.push_back(vertex_ofs + rindices[j * 3 + 0]); + indices.push_back(vertex_ofs + rindices[j * 3 + 1]); + indices.push_back(vertex_ofs + rindices[j * 3 + 2]); } } @@ -1499,6 +1512,9 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach //unwrap + bool use_cache = p_generate_cache; // Used to request cache generation and to know if cache was used + uint8_t *gen_cache; + int gen_cache_size; float *gen_uvs; int *gen_vertices; int *gen_indices; @@ -1507,17 +1523,16 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach int size_x; int size_y; - bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache); + bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y); if (!ok) { return ERR_CANT_CREATE; } - //remove surfaces clear_surfaces(); //create surfacetools for each surface.. - Vector<Ref<SurfaceTool>> surfaces_tools; + LocalVector<Ref<SurfaceTool>> surfaces_tools; for (int i = 0; i < lightmap_surfaces.size(); i++) { Ref<SurfaceTool> st; @@ -1528,11 +1543,12 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach } print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); + //go through all indices for (int i = 0; i < gen_index_count; i += 3) { - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG); - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG); - ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG); + ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG); ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG); @@ -1542,48 +1558,53 @@ Error ArrayMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cach SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second]; if (lightmap_surfaces[surface].format & ARRAY_FORMAT_COLOR) { - surfaces_tools.write[surface]->set_color(v.color); + surfaces_tools[surface]->set_color(v.color); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { - surfaces_tools.write[surface]->set_uv(v.uv); + surfaces_tools[surface]->set_uv(v.uv); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_NORMAL) { - surfaces_tools.write[surface]->set_normal(v.normal); + surfaces_tools[surface]->set_normal(v.normal); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_TANGENT) { Plane t; t.normal = v.tangent; t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; - surfaces_tools.write[surface]->set_tangent(t); + surfaces_tools[surface]->set_tangent(t); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_BONES) { - surfaces_tools.write[surface]->set_bones(v.bones); + surfaces_tools[surface]->set_bones(v.bones); } if (lightmap_surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { - surfaces_tools.write[surface]->set_weights(v.weights); + surfaces_tools[surface]->set_weights(v.weights); } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); - surfaces_tools.write[surface]->set_uv2(uv2); + surfaces_tools[surface]->set_uv2(uv2); - surfaces_tools.write[surface]->add_vertex(v.vertex); + surfaces_tools[surface]->add_vertex(v.vertex); } } //generate surfaces - - for (int i = 0; i < surfaces_tools.size(); i++) { - surfaces_tools.write[i]->index(); - surfaces_tools.write[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format); + for (unsigned int i = 0; i < surfaces_tools.size(); i++) { + surfaces_tools[i]->index(); + surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), lightmap_surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); - if (!r_used_cache) { - //free stuff - ::free(gen_vertices); - ::free(gen_indices); - ::free(gen_uvs); + if (gen_cache_size > 0) { + r_dst_cache.resize(gen_cache_size); + memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size); + memfree(gen_cache); + } + + if (!use_cache) { + // Cache was not used, free the buffers + memfree(gen_vertices); + memfree(gen_indices); + memfree(gen_uvs); } return OK; diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 9a462d5719..aa830d7b50 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -165,6 +165,9 @@ public: Vector<Ref<Shape3D>> convex_decompose() const; + virtual int get_builtin_bind_pose_count() const; + virtual Transform get_builtin_bind_pose(int p_index) const; + Mesh(); }; @@ -260,7 +263,7 @@ public: void regen_normal_maps(); Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); - Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform = Transform(), float p_texel_size = 0.05); + Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache, bool p_generate_cache = true); virtual void reload_from_file() override; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 8c12f59a00..0a25bb2ed1 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -92,6 +92,7 @@ uint32_t NavigationMesh::get_collision_mask() const { } void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { + ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { mask |= 1 << p_bit; @@ -102,6 +103,7 @@ void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { } bool NavigationMesh::get_collision_mask_bit(int p_bit) const { + ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); return get_collision_mask() & (1 << p_bit); } diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp index d65b0c8927..31df35aa51 100644 --- a/scene/resources/physics_material.cpp +++ b/scene/resources/physics_material.cpp @@ -43,9 +43,9 @@ void PhysicsMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_absorbent", "absorbent"), &PhysicsMaterial::set_absorbent); ClassDB::bind_method(D_METHOD("is_absorbent"), &PhysicsMaterial::is_absorbent); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rough"), "set_rough", "is_rough"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "absorbent"), "set_absorbent", "is_absorbent"); } diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 1be511e8f1..c3d84aeda2 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1538,3 +1538,552 @@ void PointMesh::_create_mesh_array(Array &p_arr) const { PointMesh::PointMesh() { primitive_type = PRIMITIVE_POINTS; } +// TUBE TRAIL + +void TubeTrailMesh::set_radius(const float p_radius) { + radius = p_radius; + _request_update(); +} +float TubeTrailMesh::get_radius() const { + return radius; +} + +void TubeTrailMesh::set_radial_steps(const int p_radial_steps) { + ERR_FAIL_COND(p_radial_steps < 3 || p_radial_steps > 128); + radial_steps = p_radial_steps; + _request_update(); +} +int TubeTrailMesh::get_radial_steps() const { + return radial_steps; +} + +void TubeTrailMesh::set_sections(const int p_sections) { + ERR_FAIL_COND(p_sections < 2 || p_sections > 128); + sections = p_sections; + _request_update(); +} +int TubeTrailMesh::get_sections() const { + return sections; +} + +void TubeTrailMesh::set_section_length(float p_section_length) { + section_length = p_section_length; + _request_update(); +} +float TubeTrailMesh::get_section_length() const { + return section_length; +} + +void TubeTrailMesh::set_section_rings(const int p_section_rings) { + ERR_FAIL_COND(p_section_rings < 1 || p_section_rings > 1024); + section_rings = p_section_rings; + _request_update(); +} +int TubeTrailMesh::get_section_rings() const { + return section_rings; +} + +void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) { + if (curve == p_curve) { + return; + } + if (curve.is_valid()) { + curve->disconnect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed)); + } + curve = p_curve; + if (curve.is_valid()) { + curve->connect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed)); + } + _request_update(); +} +Ref<Curve> TubeTrailMesh::get_curve() const { + return curve; +} + +void TubeTrailMesh::_curve_changed() { + _request_update(); +} +int TubeTrailMesh::get_builtin_bind_pose_count() const { + return sections + 1; +} + +Transform TubeTrailMesh::get_builtin_bind_pose(int p_index) const { + float depth = section_length * sections; + + Transform xform; + xform.origin.y = depth / 2.0 - section_length * float(p_index); + xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y + + return xform; +} + +void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { + PackedVector3Array points; + PackedVector3Array normals; + PackedFloat32Array tangents; + PackedVector2Array uvs; + PackedInt32Array bone_indices; + PackedFloat32Array bone_weights; + PackedInt32Array indices; + + int point = 0; + +#define ADD_TANGENT(m_x, m_y, m_z, m_d) \ + tangents.push_back(m_x); \ + tangents.push_back(m_y); \ + tangents.push_back(m_z); \ + tangents.push_back(m_d); + + int thisrow = 0; + int prevrow = 0; + + int total_rings = section_rings * sections; + float depth = section_length * sections; + + for (int j = 0; j <= total_rings; j++) { + float v = j; + v /= total_rings; + + float y = depth * v; + y = (depth * 0.5) - y; + + int bone = j / section_rings; + float blend = 1.0 - float(j % section_rings) / float(section_rings); + + for (int i = 0; i <= radial_steps; i++) { + float u = i; + u /= radial_steps; + + float r = radius; + if (curve.is_valid() && curve->get_point_count() > 0) { + r *= curve->interpolate_baked(v); + } + float x = sin(u * Math_TAU); + float z = cos(u * Math_TAU); + + Vector3 p = Vector3(x * r, y, z * r); + points.push_back(p); + normals.push_back(Vector3(x, 0, z)); + ADD_TANGENT(z, 0.0, -x, 1.0) + uvs.push_back(Vector2(u, v * 0.5)); + point++; + { + bone_indices.push_back(bone); + bone_indices.push_back(MIN(sections, bone + 1)); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(blend); + bone_weights.push_back(1.0 - blend); + bone_weights.push_back(0); + bone_weights.push_back(0); + } + + if (i > 0 && j > 0) { + indices.push_back(prevrow + i - 1); + indices.push_back(prevrow + i); + indices.push_back(thisrow + i - 1); + + indices.push_back(prevrow + i); + indices.push_back(thisrow + i); + indices.push_back(thisrow + i - 1); + } + } + + prevrow = thisrow; + thisrow = point; + } + + // add top + float scale_pos = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_pos = curve->interpolate_baked(0); + } + + if (scale_pos > CMP_EPSILON) { + float y = depth * 0.5; + + thisrow = point; + points.push_back(Vector3(0.0, y, 0)); + normals.push_back(Vector3(0.0, 1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(0.25, 0.75)); + point++; + + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + float rm = radius * scale_pos; + + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; + + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); + + float u = ((x + 1.0) * 0.25); + float v = 0.5 + ((z + 1.0) * 0.25); + + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, 1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; + + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 1); + indices.push_back(point - 2); + }; + }; + }; + + float scale_neg = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_neg = curve->interpolate_baked(1.0); + } + + // add bottom + if (scale_neg > CMP_EPSILON) { + float y = depth * -0.5; + + thisrow = point; + points.push_back(Vector3(0.0, y, 0.0)); + normals.push_back(Vector3(0.0, -1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(0.75, 0.75)); + point++; + + bone_indices.push_back(sections); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + float rm = radius * scale_neg; + + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; + + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); + + float u = 0.5 + ((x + 1.0) * 0.25); + float v = 1.0 - ((z + 1.0) * 0.25); + + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, -1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; + + bone_indices.push_back(sections); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 2); + indices.push_back(point - 1); + }; + }; + }; + + p_arr[RS::ARRAY_VERTEX] = points; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_BONES] = bone_indices; + p_arr[RS::ARRAY_WEIGHTS] = bone_weights; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void TubeTrailMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &TubeTrailMesh::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &TubeTrailMesh::get_radius); + + ClassDB::bind_method(D_METHOD("set_radial_steps", "radial_steps"), &TubeTrailMesh::set_radial_steps); + ClassDB::bind_method(D_METHOD("get_radial_steps"), &TubeTrailMesh::get_radial_steps); + + ClassDB::bind_method(D_METHOD("set_sections", "sections"), &TubeTrailMesh::set_sections); + ClassDB::bind_method(D_METHOD("get_sections"), &TubeTrailMesh::get_sections); + + ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &TubeTrailMesh::set_section_length); + ClassDB::bind_method(D_METHOD("get_section_length"), &TubeTrailMesh::get_section_length); + + ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings); + ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings); + + ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve); + ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_radius", "get_radius"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_steps", PROPERTY_HINT_RANGE, "3,128,1"), "set_radial_steps", "get_radial_steps"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections"); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings"); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); +} + +TubeTrailMesh::TubeTrailMesh() { +} + +// TUBE TRAIL + +void RibbonTrailMesh::set_shape(Shape p_shape) { + shape = p_shape; + _request_update(); +} +RibbonTrailMesh::Shape RibbonTrailMesh::get_shape() const { + return shape; +} + +void RibbonTrailMesh::set_size(const float p_size) { + size = p_size; + _request_update(); +} +float RibbonTrailMesh::get_size() const { + return size; +} + +void RibbonTrailMesh::set_sections(const int p_sections) { + ERR_FAIL_COND(p_sections < 2 || p_sections > 128); + sections = p_sections; + _request_update(); +} +int RibbonTrailMesh::get_sections() const { + return sections; +} + +void RibbonTrailMesh::set_section_length(float p_section_length) { + section_length = p_section_length; + _request_update(); +} +float RibbonTrailMesh::get_section_length() const { + return section_length; +} + +void RibbonTrailMesh::set_section_segments(const int p_section_segments) { + ERR_FAIL_COND(p_section_segments < 1 || p_section_segments > 1024); + section_segments = p_section_segments; + _request_update(); +} +int RibbonTrailMesh::get_section_segments() const { + return section_segments; +} + +void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) { + if (curve == p_curve) { + return; + } + if (curve.is_valid()) { + curve->disconnect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed)); + } + curve = p_curve; + if (curve.is_valid()) { + curve->connect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed)); + } + _request_update(); +} +Ref<Curve> RibbonTrailMesh::get_curve() const { + return curve; +} + +void RibbonTrailMesh::_curve_changed() { + _request_update(); +} +int RibbonTrailMesh::get_builtin_bind_pose_count() const { + return sections + 1; +} + +Transform RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { + float depth = section_length * sections; + + Transform xform; + xform.origin.y = depth / 2.0 - section_length * float(p_index); + xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y + + return xform; +} + +void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { + PackedVector3Array points; + PackedVector3Array normals; + PackedFloat32Array tangents; + PackedVector2Array uvs; + PackedInt32Array bone_indices; + PackedFloat32Array bone_weights; + PackedInt32Array indices; + +#define ADD_TANGENT(m_x, m_y, m_z, m_d) \ + tangents.push_back(m_x); \ + tangents.push_back(m_y); \ + tangents.push_back(m_z); \ + tangents.push_back(m_d); + + int total_segments = section_segments * sections; + float depth = section_length * sections; + + for (int j = 0; j <= total_segments; j++) { + float v = j; + v /= total_segments; + + float y = depth * v; + y = (depth * 0.5) - y; + + int bone = j / section_segments; + float blend = 1.0 - float(j % section_segments) / float(section_segments); + + float s = size; + + if (curve.is_valid() && curve->get_point_count() > 0) { + s *= curve->interpolate_baked(v); + } + + points.push_back(Vector3(-s * 0.5, y, 0)); + points.push_back(Vector3(+s * 0.5, y, 0)); + if (shape == SHAPE_CROSS) { + points.push_back(Vector3(0, y, -s * 0.5)); + points.push_back(Vector3(0, y, +s * 0.5)); + } + + normals.push_back(Vector3(0, 0, 1)); + normals.push_back(Vector3(0, 0, 1)); + if (shape == SHAPE_CROSS) { + normals.push_back(Vector3(1, 0, 0)); + normals.push_back(Vector3(1, 0, 0)); + } + + uvs.push_back(Vector2(0, v)); + uvs.push_back(Vector2(1, v)); + if (shape == SHAPE_CROSS) { + uvs.push_back(Vector2(0, v)); + uvs.push_back(Vector2(1, v)); + } + + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + if (shape == SHAPE_CROSS) { + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + ADD_TANGENT(0.0, 1.0, 0.0, 1.0) + } + + for (int i = 0; i < (shape == SHAPE_CROSS ? 4 : 2); i++) { + bone_indices.push_back(bone); + bone_indices.push_back(MIN(sections, bone + 1)); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(blend); + bone_weights.push_back(1.0 - blend); + bone_weights.push_back(0); + bone_weights.push_back(0); + } + + if (j > 0) { + if (shape == SHAPE_CROSS) { + int base = j * 4 - 4; + indices.push_back(base + 0); + indices.push_back(base + 1); + indices.push_back(base + 4); + + indices.push_back(base + 1); + indices.push_back(base + 5); + indices.push_back(base + 4); + + indices.push_back(base + 2); + indices.push_back(base + 3); + indices.push_back(base + 6); + + indices.push_back(base + 3); + indices.push_back(base + 7); + indices.push_back(base + 6); + } else { + int base = j * 2 - 2; + indices.push_back(base + 0); + indices.push_back(base + 1); + indices.push_back(base + 2); + + indices.push_back(base + 1); + indices.push_back(base + 3); + indices.push_back(base + 2); + } + } + } + + p_arr[RS::ARRAY_VERTEX] = points; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_BONES] = bone_indices; + p_arr[RS::ARRAY_WEIGHTS] = bone_weights; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void RibbonTrailMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &RibbonTrailMesh::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &RibbonTrailMesh::get_size); + + ClassDB::bind_method(D_METHOD("set_sections", "sections"), &RibbonTrailMesh::set_sections); + ClassDB::bind_method(D_METHOD("get_sections"), &RibbonTrailMesh::get_sections); + + ClassDB::bind_method(D_METHOD("set_section_length", "section_length"), &RibbonTrailMesh::set_section_length); + ClassDB::bind_method(D_METHOD("get_section_length"), &RibbonTrailMesh::get_section_length); + + ClassDB::bind_method(D_METHOD("set_section_segments", "section_segments"), &RibbonTrailMesh::set_section_segments); + ClassDB::bind_method(D_METHOD("get_section_segments"), &RibbonTrailMesh::get_section_segments); + + ClassDB::bind_method(D_METHOD("set_curve", "curve"), &RibbonTrailMesh::set_curve); + ClassDB::bind_method(D_METHOD("get_curve"), &RibbonTrailMesh::get_curve); + + ClassDB::bind_method(D_METHOD("set_shape", "shape"), &RibbonTrailMesh::set_shape); + ClassDB::bind_method(D_METHOD("get_shape"), &RibbonTrailMesh::get_shape); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "shape", PROPERTY_HINT_ENUM, "Flat,Cross"), "set_shape", "get_shape"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,or_greater"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_sections", "get_sections"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "section_length", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001,or_greater"), "set_section_length", "get_section_length"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "section_segments", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_segments", "get_section_segments"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); + + BIND_ENUM_CONSTANT(SHAPE_FLAT) + BIND_ENUM_CONSTANT(SHAPE_CROSS) +} + +RibbonTrailMesh::RibbonTrailMesh() { +} diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 65ecdfc19d..ec5806489e 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -336,4 +336,98 @@ public: PointMesh(); }; +class TubeTrailMesh : public PrimitiveMesh { + GDCLASS(TubeTrailMesh, PrimitiveMesh); + +private: + float radius = 1.0; + int radial_steps = 8; + int sections = 5; + float section_length = 0.2; + int section_rings = 3; + + Ref<Curve> curve; + + void _curve_changed(); + +protected: + static void _bind_methods(); + virtual void _create_mesh_array(Array &p_arr) const override; + +public: + void set_radius(const float p_radius); + float get_radius() const; + + void set_radial_steps(const int p_radial_steps); + int get_radial_steps() const; + + void set_sections(const int p_sections); + int get_sections() const; + + void set_section_length(float p_sectionlength); + float get_section_length() const; + + void set_section_rings(const int p_section_rings); + int get_section_rings() const; + + void set_curve(const Ref<Curve> &p_curve); + Ref<Curve> get_curve() const; + + virtual int get_builtin_bind_pose_count() const override; + virtual Transform get_builtin_bind_pose(int p_index) const override; + + TubeTrailMesh(); +}; + +class RibbonTrailMesh : public PrimitiveMesh { + GDCLASS(RibbonTrailMesh, PrimitiveMesh); + +public: + enum Shape { + SHAPE_FLAT, + SHAPE_CROSS + }; + +private: + float size = 1.0; + int sections = 5; + float section_length = 0.2; + int section_segments = 3; + + Shape shape = SHAPE_CROSS; + + Ref<Curve> curve; + + void _curve_changed(); + +protected: + static void _bind_methods(); + virtual void _create_mesh_array(Array &p_arr) const override; + +public: + void set_shape(Shape p_shape); + Shape get_shape() const; + + void set_size(const float p_size); + float get_size() const; + + void set_sections(const int p_sections); + int get_sections() const; + + void set_section_length(float p_sectionlength); + float get_section_length() const; + + void set_section_segments(const int p_section_segments); + int get_section_segments() const; + + void set_curve(const Ref<Curve> &p_curve); + Ref<Curve> get_curve() const; + + virtual int get_builtin_bind_pose_count() const override; + virtual Transform get_builtin_bind_pose(int p_index) const override; + + RibbonTrailMesh(); +}; + +VARIANT_ENUM_CAST(RibbonTrailMesh::Shape) #endif diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 771365152d..624eae0411 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1405,185 +1405,6 @@ MeshTexture::MeshTexture() { ////////////////////////////////////////// -int LargeTexture::get_width() const { - return size.width; -} - -int LargeTexture::get_height() const { - return size.height; -} - -RID LargeTexture::get_rid() const { - return RID(); -} - -bool LargeTexture::has_alpha() const { - for (int i = 0; i < pieces.size(); i++) { - if (pieces[i].texture->has_alpha()) { - return true; - } - } - - return false; -} - -int LargeTexture::add_piece(const Point2 &p_offset, const Ref<Texture2D> &p_texture) { - ERR_FAIL_COND_V(p_texture.is_null(), -1); - Piece p; - p.offset = p_offset; - p.texture = p_texture; - pieces.push_back(p); - - return pieces.size() - 1; -} - -void LargeTexture::set_piece_offset(int p_idx, const Point2 &p_offset) { - ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces.write[p_idx].offset = p_offset; -}; - -void LargeTexture::set_piece_texture(int p_idx, const Ref<Texture2D> &p_texture) { - ERR_FAIL_COND(p_texture == this); - ERR_FAIL_COND(p_texture.is_null()); - ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces.write[p_idx].texture = p_texture; -}; - -void LargeTexture::set_size(const Size2 &p_size) { - size = p_size; -} - -void LargeTexture::clear() { - pieces.clear(); - size = Size2i(); -} - -Array LargeTexture::_get_data() const { - Array arr; - for (int i = 0; i < pieces.size(); i++) { - arr.push_back(pieces[i].offset); - arr.push_back(pieces[i].texture); - } - arr.push_back(Size2(size)); - return arr; -} - -void LargeTexture::_set_data(const Array &p_array) { - ERR_FAIL_COND(p_array.size() < 1); - ERR_FAIL_COND(!(p_array.size() & 1)); - clear(); - for (int i = 0; i < p_array.size() - 1; i += 2) { - add_piece(p_array[i], p_array[i + 1]); - } - size = Size2(p_array[p_array.size() - 1]); -} - -int LargeTexture::get_piece_count() const { - return pieces.size(); -} - -Vector2 LargeTexture::get_piece_offset(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, pieces.size(), Vector2()); - return pieces[p_idx].offset; -} - -Ref<Texture2D> LargeTexture::get_piece_texture(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, pieces.size(), Ref<Texture2D>()); - return pieces[p_idx].texture; -} - -Ref<Image> LargeTexture::to_image() const { - Ref<Image> img = memnew(Image(this->get_width(), this->get_height(), false, Image::FORMAT_RGBA8)); - for (int i = 0; i < pieces.size(); i++) { - Ref<Image> src_img = pieces[i].texture->get_image(); - img->blit_rect(src_img, Rect2(0, 0, src_img->get_width(), src_img->get_height()), pieces[i].offset); - } - - return img; -} - -void LargeTexture::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_piece", "ofs", "texture"), &LargeTexture::add_piece); - ClassDB::bind_method(D_METHOD("set_piece_offset", "idx", "ofs"), &LargeTexture::set_piece_offset); - ClassDB::bind_method(D_METHOD("set_piece_texture", "idx", "texture"), &LargeTexture::set_piece_texture); - ClassDB::bind_method(D_METHOD("set_size", "size"), &LargeTexture::set_size); - ClassDB::bind_method(D_METHOD("clear"), &LargeTexture::clear); - - ClassDB::bind_method(D_METHOD("get_piece_count"), &LargeTexture::get_piece_count); - ClassDB::bind_method(D_METHOD("get_piece_offset", "idx"), &LargeTexture::get_piece_offset); - ClassDB::bind_method(D_METHOD("get_piece_texture", "idx"), &LargeTexture::get_piece_texture); - - ClassDB::bind_method(D_METHOD("_set_data", "data"), &LargeTexture::_set_data); - ClassDB::bind_method(D_METHOD("_get_data"), &LargeTexture::_get_data); - - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); -} - -void LargeTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const { - for (int i = 0; i < pieces.size(); i++) { - // TODO - pieces[i].texture->draw(p_canvas_item, pieces[i].offset + p_pos, p_modulate, p_transpose); - } -} - -void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const { - //tiling not supported for this - if (size.x == 0 || size.y == 0) { - return; - } - - Size2 scale = p_rect.size / size; - - for (int i = 0; i < pieces.size(); i++) { - // TODO - pieces[i].texture->draw_rect(p_canvas_item, Rect2(pieces[i].offset * scale + p_rect.position, pieces[i].texture->get_size() * scale), false, p_modulate, p_transpose); - } -} - -void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const { - //tiling not supported for this - if (p_src_rect.size.x == 0 || p_src_rect.size.y == 0) { - return; - } - - Size2 scale = p_rect.size / p_src_rect.size; - - for (int i = 0; i < pieces.size(); i++) { - // TODO - Rect2 rect(pieces[i].offset, pieces[i].texture->get_size()); - if (!p_src_rect.intersects(rect)) { - continue; - } - Rect2 local = p_src_rect.intersection(rect); - Rect2 target = local; - target.size *= scale; - target.position = p_rect.position + (p_src_rect.position + rect.position) * scale; - local.position -= rect.position; - pieces[i].texture->draw_rect_region(p_canvas_item, target, local, p_modulate, p_transpose, false); - } -} - -bool LargeTexture::is_pixel_opaque(int p_x, int p_y) const { - for (int i = 0; i < pieces.size(); i++) { - // TODO - if (!pieces[i].texture.is_valid()) { - continue; - } - - Rect2 rect(pieces[i].offset, pieces[i].texture->get_size()); - if (rect.has_point(Point2(p_x, p_y))) { - return pieces[i].texture->is_pixel_opaque(p_x - rect.position.x, p_y - rect.position.y); - } - } - - return true; -} - -LargeTexture::LargeTexture() { -} - -/////////////////// - void CurveTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 16c98f2891..264d85d187 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -297,51 +297,6 @@ public: MeshTexture(); }; -class LargeTexture : public Texture2D { - GDCLASS(LargeTexture, Texture2D); - RES_BASE_EXTENSION("largetex"); - -protected: - struct Piece { - Point2 offset; - Ref<Texture2D> texture; - }; - - Vector<Piece> pieces; - Size2i size; - - Array _get_data() const; - void _set_data(const Array &p_array); - static void _bind_methods(); - -public: - virtual int get_width() const override; - virtual int get_height() const override; - virtual RID get_rid() const override; - - virtual bool has_alpha() const override; - - int add_piece(const Point2 &p_offset, const Ref<Texture2D> &p_texture); - void set_piece_offset(int p_idx, const Point2 &p_offset); - void set_piece_texture(int p_idx, const Ref<Texture2D> &p_texture); - - void set_size(const Size2 &p_size); - void clear(); - - int get_piece_count() const; - Vector2 get_piece_offset(int p_idx) const; - Ref<Texture2D> get_piece_texture(int p_idx) const; - Ref<Image> to_image() const; - - virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override; - virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override; - virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override; - - bool is_pixel_opaque(int p_x, int p_y) const override; - - LargeTexture(); -}; - class TextureLayered : public Texture { GDCLASS(TextureLayered, Texture); |