diff options
48 files changed, 423 insertions, 220 deletions
diff --git a/core/io/resource.cpp b/core/io/resource.cpp index e44bbc246b..4abcbffb61 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -260,15 +260,35 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { } Variant p = get(E.name); - if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { - r->set(E.name, p.duplicate(p_subresources)); - } else if (p.get_type() == Variant::OBJECT && !(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) { - Ref<Resource> sr = p; - if (sr.is_valid()) { - r->set(E.name, sr->duplicate(p_subresources)); + switch (p.get_type()) { + case Variant::Type::DICTIONARY: + case Variant::Type::ARRAY: + case Variant::Type::PACKED_BYTE_ARRAY: + case Variant::Type::PACKED_COLOR_ARRAY: + case Variant::Type::PACKED_INT32_ARRAY: + case Variant::Type::PACKED_INT64_ARRAY: + case Variant::Type::PACKED_FLOAT32_ARRAY: + case Variant::Type::PACKED_FLOAT64_ARRAY: + case Variant::Type::PACKED_STRING_ARRAY: + case Variant::Type::PACKED_VECTOR2_ARRAY: + case Variant::Type::PACKED_VECTOR3_ARRAY: { + r->set(E.name, p.duplicate(p_subresources)); + } break; + + case Variant::Type::OBJECT: { + if (!(E.usage & PROPERTY_USAGE_NEVER_DUPLICATE) && (p_subresources || (E.usage & PROPERTY_USAGE_ALWAYS_DUPLICATE))) { + Ref<Resource> sr = p; + if (sr.is_valid()) { + r->set(E.name, sr->duplicate(p_subresources)); + } + } else { + r->set(E.name, p); + } + } break; + + default: { + r->set(E.name, p); } - } else { - r->set(E.name, p); } } diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 910995d717..96010b4096 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -263,39 +263,12 @@ real_t Transform2D::basis_determinant() const { return columns[0].x * columns[1].y - columns[0].y * columns[1].x; } -Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_c) const { - //extract parameters - Vector2 p1 = get_origin(); - Vector2 p2 = p_transform.get_origin(); - - real_t r1 = get_rotation(); - real_t r2 = p_transform.get_rotation(); - - Size2 s1 = get_scale(); - Size2 s2 = p_transform.get_scale(); - - //slerp rotation - Vector2 v1(Math::cos(r1), Math::sin(r1)); - Vector2 v2(Math::cos(r2), Math::sin(r2)); - - real_t dot = v1.dot(v2); - - dot = CLAMP(dot, (real_t)-1.0, (real_t)1.0); - - Vector2 v; - - if (dot > 0.9995f) { - v = v1.lerp(v2, p_c).normalized(); //linearly interpolate to avoid numerical precision issues - } else { - real_t angle = p_c * Math::acos(dot); - Vector2 v3 = (v2 - v1 * dot).normalized(); - v = v1 * Math::cos(angle) + v3 * Math::sin(angle); - } - - //construct matrix - Transform2D res(v.angle(), p1.lerp(p2, p_c)); - res.scale_basis(s1.lerp(s2, p_c)); - return res; +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_weight) const { + return Transform2D( + Math::lerp_angle(get_rotation(), p_transform.get_rotation(), p_weight), + get_scale().lerp(p_transform.get_scale(), p_weight), + Math::lerp_angle(get_skew(), p_transform.get_skew(), p_weight), + get_origin().lerp(p_transform.get_origin(), p_weight)); } void Transform2D::operator*=(const real_t p_val) { diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 8888d11a24..c65c396591 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -113,16 +113,19 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ // Clear out any state that may have been left from the 3D pass. reset_canvas(); - if (state.canvas_instance_data_buffers[state.current_buffer].fence != GLsync()) { + if (state.canvas_instance_data_buffers[state.current_data_buffer_index].fence != GLsync()) { GLint syncStatus; - glGetSynciv(state.canvas_instance_data_buffers[state.current_buffer].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + glGetSynciv(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); if (syncStatus == GL_UNSIGNALED) { // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway. - if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { + if (state.canvas_instance_data_buffers[state.current_data_buffer_index].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { #ifndef WEB_ENABLED // On web, we do nothing as the glSubBufferData will force a sync anyway and WebGL does not like waiting. - glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms + glClientWaitSync(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence, 0, 100000000); // wait for up to 100ms #endif + state.canvas_instance_data_buffers[state.current_data_buffer_index].last_frame_used = RSG::rasterizer->get_frame_number(); + glDeleteSync(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence); + state.canvas_instance_data_buffers[state.current_data_buffer_index].fence = GLsync(); } else { // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use // Allocate a new buffer and use that. @@ -130,9 +133,9 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } } else { // Already finished all rendering commands, we can use it. - state.canvas_instance_data_buffers[state.current_buffer].last_frame_used = RSG::rasterizer->get_frame_number(); - glDeleteSync(state.canvas_instance_data_buffers[state.current_buffer].fence); - state.canvas_instance_data_buffers[state.current_buffer].fence = GLsync(); + state.canvas_instance_data_buffers[state.current_data_buffer_index].last_frame_used = RSG::rasterizer->get_frame_number(); + glDeleteSync(state.canvas_instance_data_buffers[state.current_data_buffer_index].fence); + state.canvas_instance_data_buffers[state.current_data_buffer_index].fence = GLsync(); } } @@ -279,7 +282,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (light_count > 0) { - glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].light_ubo); + glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_data_buffer_index].light_ubo); #ifdef WEB_ENABLED glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, state.light_uniforms); @@ -361,7 +364,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].state_ubo); + glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_data_buffer_index].state_ubo); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); GLuint global_buffer = material_storage->global_shader_parameters_get_uniform_buffer(); @@ -395,7 +398,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ Item *ci = p_item_list; Item *canvas_group_owner = nullptr; - uint32_t starting_index = 0; + state.last_item_index = 0; while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { @@ -455,7 +458,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ update_skeletons = false; } // Canvas group begins here, render until before this item - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); item_count = 0; if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { @@ -486,7 +489,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ mesh_storage->update_mesh_instances(); update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used, true); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used, true); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -505,7 +508,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } //render anything pending, including clearing if no items - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); item_count = 0; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); @@ -531,7 +534,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ mesh_storage->update_mesh_instances(); update_skeletons = false; } - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, r_sdf_used); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, r_sdf_used); //then reset item_count = 0; } @@ -543,14 +546,15 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ RenderingServerDefault::redraw_request(); } - state.canvas_instance_data_buffers[state.current_buffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + state.canvas_instance_data_buffers[state.current_data_buffer_index].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Clear out state used in 2D pass reset_canvas(); - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + state.current_data_buffer_index = (state.current_data_buffer_index + 1) % state.canvas_instance_data_buffers.size(); + state.current_instance_buffer_index = 0; } -void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool &r_sdf_used, bool p_to_backbuffer) { +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); canvas_begin(p_to_render_target, p_to_backbuffer); @@ -566,17 +570,17 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou // Record Batches. // First item always forms its own batch. bool batch_broken = false; - _new_batch(batch_broken, index); + _new_batch(batch_broken); // Override the start position and index as we want to start from where we finished off last time. - state.canvas_instance_batches[state.current_batch_index].start = r_last_index; + state.canvas_instance_batches[state.current_batch_index].start = state.last_item_index; index = 0; for (int i = 0; i < p_item_count; i++) { Item *ci = items[i]; if (ci->final_clip_owner != state.canvas_instance_batches[state.current_batch_index].clip) { - _new_batch(batch_broken, index); + _new_batch(batch_broken); state.canvas_instance_batches[state.current_batch_index].clip = ci->final_clip_owner; current_clip = ci->final_clip_owner; } @@ -600,7 +604,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou GLES3::CanvasShaderData *shader_data_cache = nullptr; if (material != state.canvas_instance_batches[state.current_batch_index].material) { - _new_batch(batch_broken, index); + _new_batch(batch_broken); GLES3::CanvasMaterialData *material_data = nullptr; if (material.is_valid()) { @@ -630,12 +634,12 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou } // Copy over all data needed for rendering. - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index]); #ifdef WEB_ENABLED - glBufferSubData(GL_ARRAY_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); + glBufferSubData(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); #else // On Desktop and mobile we map the memory without synchronizing for maximum speed. - void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); memcpy(buffer, state.instance_data_array, index * sizeof(InstanceData)); glUnmapBuffer(GL_ARRAY_BUFFER); #endif @@ -758,14 +762,14 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou state.current_batch_index = 0; state.canvas_instance_batches.clear(); - r_last_index += index; + state.last_item_index += index; } void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used) { RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].filter = texture_filter; } @@ -773,7 +777,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? state.default_repeat : p_item->texture_repeat; if (texture_repeat != state.canvas_instance_batches[state.current_batch_index].repeat) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat; } @@ -817,7 +821,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend bool lights_disabled = light_count == 0 && !state.using_directional_lights; if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled; } @@ -865,7 +869,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend } if (blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].blend_mode = blend_mode; state.canvas_instance_batches[state.current_batch_index].blend_color = blend_color; } @@ -875,12 +879,12 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); if (rect->flags & CANVAS_RECT_TILE && state.canvas_instance_batches[state.current_batch_index].repeat != RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; } if (rect->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_RECT) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = rect->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT; state.canvas_instance_batches[state.current_batch_index].command = c; @@ -970,7 +974,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); if (np->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_NINEPATCH) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = np->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH; state.canvas_instance_batches[state.current_batch_index].command = c; @@ -1035,7 +1039,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); // Polygon's can't be batched, so always create a new batch - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON; @@ -1062,7 +1066,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); if (primitive->point_count != state.canvas_instance_batches[state.current_batch_index].primitive_points || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_PRIMITIVE) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); state.canvas_instance_batches[state.current_batch_index].tex = primitive->texture; state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count; state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE; @@ -1087,10 +1091,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend if (primitive->point_count == 4) { // Reset base data. _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); - state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; - state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; - - state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); for (uint32_t j = 0; j < 3; j++) { int offset = j == 0 ? 0 : 1; @@ -1112,7 +1113,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend case Item::Command::TYPE_MULTIMESH: case Item::Command::TYPE_PARTICLES: { // Mesh's can't be batched, so always create a new batch - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); Color modulate(1, 1, 1, 1); state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; @@ -1184,7 +1185,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); if (current_clip) { if (ci->ignore != reclip) { - _new_batch(r_batch_broken, r_index); + _new_batch(r_batch_broken); if (ci->ignore) { state.canvas_instance_batches[state.current_batch_index].clip = nullptr; reclip = true; @@ -1228,7 +1229,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { case Item::Command::TYPE_RECT: case Item::Command::TYPE_NINEPATCH: { glBindVertexArray(data.indexed_quad_array); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, false); @@ -1244,7 +1245,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { ERR_FAIL_COND(!pb); glBindVertexArray(pb->vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, false); @@ -1268,7 +1269,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { case Item::Command::TYPE_PRIMITIVE: { glBindVertexArray(data.canvas_quad_array); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, true); @@ -1371,7 +1372,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); bool use_index_buffer = false; glBindVertexArray(vertex_array_gl); - glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].buffer); + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.canvas_instance_batches[p_index].instance_buffer_index]); uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData); _enable_attributes(range_start, false, instance_count); @@ -1428,20 +1429,30 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { } void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) { - if (r_index >= data.max_instances_per_buffer - 1) { - ERR_PRINT_ONCE("Trying to draw too many items. Please increase maximum number of items in the project settings 'rendering/gl_compatibility/item_buffer_size'"); - return; - } - - if (state.canvas_instance_batches[state.current_batch_index].instance_count >= data.max_instances_per_batch) { - _new_batch(r_batch_broken, r_index); - } - state.canvas_instance_batches[state.current_batch_index].instance_count++; r_index++; + if (r_index >= data.max_instances_per_buffer) { + // Copy over all data needed for rendering right away + // then go back to recording item commands. + glBindBuffer(GL_ARRAY_BUFFER, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[state.current_instance_buffer_index]); +#ifdef WEB_ENABLED + glBufferSubData(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), sizeof(InstanceData) * r_index, state.instance_data_array); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *buffer = glMapBufferRange(GL_ARRAY_BUFFER, state.last_item_index * sizeof(InstanceData), r_index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(buffer, state.instance_data_array, r_index * sizeof(InstanceData)); + glUnmapBuffer(GL_ARRAY_BUFFER); +#endif + _allocate_instance_buffer(); + r_index = 0; + state.last_item_index = 0; + r_batch_broken = false; // Force a new batch to be created + _new_batch(r_batch_broken); + state.canvas_instance_batches[state.current_batch_index].start = 0; + } } -void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) { +void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken) { if (state.canvas_instance_batches.size() == 0) { state.canvas_instance_batches.push_back(Batch()); return; @@ -1457,7 +1468,7 @@ void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; new_batch.instance_count = 0; new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count; - + new_batch.instance_buffer_index = state.current_instance_buffer_index; state.current_batch_index++; state.canvas_instance_batches.push_back(new_batch); } @@ -2434,17 +2445,35 @@ void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); - state.current_buffer = (state.current_buffer + 1); + state.current_data_buffer_index = (state.current_data_buffer_index + 1); DataBuffer db; - db.buffer = new_buffers[0]; + db.instance_buffers.push_back(new_buffers[0]); db.light_ubo = new_buffers[1]; db.state_ubo = new_buffers[2]; db.last_frame_used = RSG::rasterizer->get_frame_number(); - state.canvas_instance_data_buffers.insert(state.current_buffer, db); - state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); + state.canvas_instance_data_buffers.insert(state.current_data_buffer_index, db); + state.current_data_buffer_index = state.current_data_buffer_index % state.canvas_instance_data_buffers.size(); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +void RasterizerCanvasGLES3::_allocate_instance_buffer() { + state.current_instance_buffer_index++; + + if (int(state.current_instance_buffer_index) < state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.size()) { + // We already allocated another buffer in a previous frame, so we can just use it. + return; + } + + GLuint new_buffer; + glGenBuffers(1, &new_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, new_buffer); + glBufferData(GL_ARRAY_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + + state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.push_back(new_buffer); + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} void RasterizerCanvasGLES3::set_time(double p_time) { state.time = p_time; @@ -2587,14 +2616,12 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { int uniform_max_size = config->max_uniform_buffer_size; if (uniform_max_size < 65536) { data.max_lights_per_render = 64; - data.max_instances_per_batch = 128; } else { data.max_lights_per_render = 256; - data.max_instances_per_batch = 2048; } // Reserve 3 Uniform Buffers for instance data Frame N, N+1 and N+2 - data.max_instances_per_buffer = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size"))); + data.max_instances_per_buffer = uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size")); data.max_instance_buffer_size = data.max_instances_per_buffer * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb state.canvas_instance_data_buffers.resize(3); state.canvas_instance_batches.reserve(200); @@ -2612,7 +2639,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); DataBuffer db; - db.buffer = new_buffers[0]; + db.instance_buffers.push_back(new_buffers[0]); db.light_ubo = new_buffers[1]; db.state_ubo = new_buffers[2]; db.last_frame_used = 0; @@ -2639,7 +2666,6 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { String global_defines; global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now global_defines += "#define MAX_LIGHTS " + itos(data.max_lights_per_render) + "\n"; - global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n"; GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines, 1); data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 916e12057c..1c14d0b466 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -247,7 +247,6 @@ public: uint32_t max_lights_per_render = 256; uint32_t max_lights_per_item = 16; - uint32_t max_instances_per_batch = 512; uint32_t max_instances_per_buffer = 16384; uint32_t max_instance_buffer_size = 16384 * 128; } data; @@ -256,6 +255,7 @@ public: // Position in the UBO measured in bytes uint32_t start = 0; uint32_t instance_count = 0; + uint32_t instance_buffer_index = 0; RID tex; RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; @@ -281,7 +281,7 @@ public: // We track them and ensure that they don't get reused until at least 2 frames have passed // to avoid the GPU stalling to wait for a resource to become available. struct DataBuffer { - GLuint buffer = 0; + Vector<GLuint> instance_buffers; GLuint light_ubo = 0; GLuint state_ubo = 0; uint64_t last_frame_used = -3; @@ -291,9 +291,10 @@ public: struct State { LocalVector<DataBuffer> canvas_instance_data_buffers; LocalVector<Batch> canvas_instance_batches; - uint32_t current_buffer = 0; - uint32_t current_buffer_index = 0; + uint32_t current_data_buffer_index = 0; + uint32_t current_instance_buffer_index = 0; uint32_t current_batch_index = 0; + uint32_t last_item_index = 0; InstanceData *instance_data_array = nullptr; @@ -354,14 +355,14 @@ public: void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size); void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override; - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool &r_sdf_used, bool p_to_backbuffer = false); + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used); void _render_batch(Light *p_lights, uint32_t p_index); bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); - void _new_batch(bool &r_batch_broken, uint32_t &r_index); + void _new_batch(bool &r_batch_broken); void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); - void _align_instance_data_buffer(uint32_t &r_index); + void _allocate_instance_buffer(); void _enable_attributes(uint32_t p_start, bool p_primitive, uint32_t p_rate = 1); void set_time(double p_time); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 30e06bfcf4..b33ad67f23 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1105,9 +1105,24 @@ void AnimationPlayerEditor::_animation_duplicate() { return; } + int count = 2; String new_name = current; - while (player->has_animation(new_name)) { - new_name = new_name + " (copy)"; + PackedStringArray split = new_name.split("_"); + int last_index = split.size() - 1; + if (last_index > 0 && split[last_index].is_valid_int() && split[last_index].to_int() >= 0) { + count = split[last_index].to_int(); + split.remove_at(last_index); + new_name = String("_").join(split); + } + while (true) { + String attempt = new_name; + attempt += vformat("_%d", count); + if (player->has_animation(attempt)) { + count++; + continue; + } + new_name = attempt; + break; } if (new_name.contains("/")) { diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 23be913a24..5883ec863d 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -307,7 +307,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_number = true; } - if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !in_number) { + if (!in_word && is_unicode_identifier_start(str[j]) && !in_number) { in_word = true; } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 7ea0603d57..8dd65a700a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1049,7 +1049,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co resolve_class_body(base_class, p_class); } - // Do functions and properties now. + // Do functions, properties, and groups now. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { @@ -1089,6 +1089,10 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co resolve_function_body(member.variable->setter); } } + } else if (member.type == GDScriptParser::ClassNode::Member::GROUP) { + // Apply annotation (`@export_{category,group,subgroup}`). + resolve_annotation(member.annotation); + member.annotation->apply(parser, nullptr); } } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 74d7f94d7c..267718207f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -840,14 +840,19 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) { case GDScriptTokenizer::Token::ANNOTATION: { advance(); - // Check for class-level annotations. + // Check for standalone and class-level annotations. AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { if (annotation->applies_to(AnnotationInfo::STANDALONE)) { if (previous.type != GDScriptTokenizer::Token::NEWLINE) { push_error(R"(Expected newline after a standalone annotation.)"); } - head->annotations.push_back(annotation); + if (annotation->name == "@export_category" || annotation->name == "@export_group" || annotation->name == "@export_subgroup") { + current_class->add_member_group(annotation); + } else { + // For potential non-group standalone annotations. + push_error(R"(Unexpected standalone annotation in class body.)"); + } } else { annotation_stack.push_back(annotation); } @@ -3841,7 +3846,6 @@ bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation } break; } - current_class->add_member_group(annotation); return true; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index e939396926..5d41e33d0b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -50,6 +50,18 @@ namespace Godot } /// <summary> + /// Returns the transform's skew (in radians). + /// </summary> + public readonly real_t Skew + { + get + { + real_t detSign = Mathf.Sign(BasisDeterminant()); + return Mathf.Acos(X.Normalized().Dot(detSign * Y.Normalized())) - Mathf.Pi * 0.5f; + } + } + + /// <summary> /// Access whole columns in the form of <see cref="Vector2"/>. /// The third column is the <see cref="Origin"/> vector. /// </summary> @@ -190,48 +202,13 @@ namespace Godot /// <returns>The interpolated transform.</returns> public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight) { - real_t r1 = Rotation; - real_t r2 = transform.Rotation; - - Vector2 s1 = Scale; - Vector2 s2 = transform.Scale; - - // Slerp rotation - (real_t sin1, real_t cos1) = Mathf.SinCos(r1); - (real_t sin2, real_t cos2) = Mathf.SinCos(r2); - var v1 = new Vector2(cos1, sin1); - var v2 = new Vector2(cos2, sin2); - - real_t dot = v1.Dot(v2); - - dot = Mathf.Clamp(dot, -1.0f, 1.0f); - - Vector2 v; - - if (dot > 0.9995f) - { - // Linearly interpolate to avoid numerical precision issues - v = v1.Lerp(v2, weight).Normalized(); - } - else - { - real_t angle = weight * Mathf.Acos(dot); - Vector2 v3 = (v2 - (v1 * dot)).Normalized(); - (real_t sine, real_t cos) = Mathf.SinCos(angle); - v = (v1 * sine) + (v3 * cos); - } - - // Extract parameters - Vector2 p1 = Origin; - Vector2 p2 = transform.Origin; - - // Construct matrix - var res = new Transform2D(Mathf.Atan2(v.Y, v.X), p1.Lerp(p2, weight)); - Vector2 scale = s1.Lerp(s2, weight); - res.X *= scale; - res.Y *= scale; - - return res; + return new Transform2D + ( + Mathf.LerpAngle(Rotation, transform.Rotation, weight), + Scale.Lerp(transform.Scale, weight), + Mathf.LerpAngle(Skew, transform.Skew, weight), + Origin.Lerp(transform.Origin, weight) + ); } /// <summary> diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml index 735ca388de..78d7d6a15b 100644 --- a/modules/noise/doc_classes/Noise.xml +++ b/modules/noise/doc_classes/Noise.xml @@ -17,8 +17,10 @@ <param index="1" name="height" type="int" /> <param index="2" name="invert" type="bool" default="false" /> <param index="3" name="in_3d_space" type="bool" default="false" /> + <param index="4" name="normalize" type="bool" default="true" /> <description> Returns a 2D [Image] noise image. + Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. </description> </method> <method name="get_noise_1d" qualifiers="const"> @@ -66,8 +68,10 @@ <param index="2" name="invert" type="bool" default="false" /> <param index="3" name="in_3d_space" type="bool" default="false" /> <param index="4" name="skirt" type="float" default="0.1" /> + <param index="5" name="normalize" type="bool" default="true" /> <description> Returns a seamless 2D [Image] noise image. + Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code]. </description> </method> </methods> diff --git a/modules/noise/doc_classes/NoiseTexture2D.xml b/modules/noise/doc_classes/NoiseTexture2D.xml index 0a800a143b..0f10a3f32f 100644 --- a/modules/noise/doc_classes/NoiseTexture2D.xml +++ b/modules/noise/doc_classes/NoiseTexture2D.xml @@ -44,6 +44,10 @@ <member name="noise" type="Noise" setter="set_noise" getter="get_noise"> The instance of the [Noise] object. </member> + <member name="normalize" type="bool" setter="set_normalize" getter="is_normalized" default="true"> + If [code]true[/code], the noise image coming from the noise generator is normalized to the range [code]0.0[/code] to [code]1.0[/code]. + Turning normalization off can affect the contrast and allows you to generate non repeating tileable noise textures. + </member> <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" /> <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false"> If [code]true[/code], a seamless texture is requested from the [Noise] resource. diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp index 5a901cb6e1..e95788b863 100644 --- a/modules/noise/noise.cpp +++ b/modules/noise/noise.cpp @@ -32,7 +32,7 @@ #include <float.h> -Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt) const { +Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const { ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>()); int skirt_width = MAX(1, p_width * p_blend_skirt); @@ -40,7 +40,7 @@ Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, b int src_width = p_width + skirt_width; int src_height = p_height + skirt_height; - Ref<Image> src = get_image(src_width, src_height, p_invert, p_in_3d_space); + Ref<Image> src = get_image(src_width, src_height, p_invert, p_in_3d_space, p_normalize); bool grayscale = (src->get_format() == Image::FORMAT_L8); if (grayscale) { return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_invert, p_blend_skirt); @@ -58,7 +58,7 @@ uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) co return (uint8_t)((alpha * p_fg + inv_alpha * p_bg) >> 8); } -Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space) const { +Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, bool p_normalize) const { ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>()); Vector<uint8_t> data; @@ -66,38 +66,49 @@ Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_ uint8_t *wd8 = data.ptrw(); - // Get all values and identify min/max values. - Vector<real_t> values; - values.resize(p_width * p_height); - real_t min_val = FLT_MAX; - real_t max_val = -FLT_MAX; - - for (int y = 0, i = 0; y < p_height; y++) { - for (int x = 0; x < p_width; x++, i++) { - values.set(i, p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); - if (values[i] > max_val) { - max_val = values[i]; - } - if (values[i] < min_val) { - min_val = values[i]; + if (p_normalize) { + // Get all values and identify min/max values. + Vector<real_t> values; + values.resize(p_width * p_height); + real_t min_val = FLT_MAX; + real_t max_val = -FLT_MAX; + for (int y = 0, i = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++, i++) { + values.set(i, p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); + if (values[i] > max_val) { + max_val = values[i]; + } + if (values[i] < min_val) { + min_val = values[i]; + } } } - } - - // Normalize values and write to texture. - uint8_t value; - for (int i = 0, x = 0; i < p_height; i++) { - for (int j = 0; j < p_width; j++, x++) { - if (max_val == min_val) { - value = 0; - } else { - value = uint8_t(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255)); + // Normalize values and write to texture. + uint8_t ivalue; + for (int i = 0, x = 0; i < p_height; i++) { + for (int j = 0; j < p_width; j++, x++) { + if (max_val == min_val) { + ivalue = 0; + } else { + ivalue = static_cast<uint8_t>(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255)); + } + + if (p_invert) { + ivalue = 255 - ivalue; + } + + wd8[x] = ivalue; } - if (p_invert) { - value = 255 - value; + } + } else { + // Without normalization, the expected range of the noise function is [-1, 1]. + uint8_t ivalue; + for (int y = 0, i = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++, i++) { + float value = (p_in_3d_space ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); + ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f)); + wd8[i] = p_invert ? (255 - ivalue) : ivalue; } - - wd8[x] = value; } } @@ -113,6 +124,6 @@ void Noise::_bind_methods() { ClassDB::bind_method(D_METHOD("get_noise_3dv", "v"), &Noise::get_noise_3dv); // Textures. - ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space"), &Noise::get_image, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1)); + ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true)); } diff --git a/modules/noise/noise.h b/modules/noise/noise.h index 8f8ecf29a5..f7e615c2aa 100644 --- a/modules/noise/noise.h +++ b/modules/noise/noise.h @@ -233,8 +233,8 @@ public: virtual real_t get_noise_3dv(Vector3 p_v) const = 0; virtual real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const = 0; - virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false) const; - virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1) const; + virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const; + virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const; }; #endif // NOISE_H diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 0eedb286bd..0d5e778875 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -76,6 +76,9 @@ void NoiseTexture2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture2D::set_bump_strength); ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture2D::get_bump_strength); + ClassDB::bind_method(D_METHOD("set_normalize", "normalize"), &NoiseTexture2D::set_normalize); + ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture2D::is_normalized); + ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture2D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture2D::get_color_ramp); @@ -91,6 +94,7 @@ void NoiseTexture2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise"); } @@ -156,9 +160,9 @@ Ref<Image> NoiseTexture2D::_generate_texture() { Ref<Image> new_image; if (seamless) { - new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt); + new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt, normalize); } else { - new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space); + new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space, normalize); } if (color_ramp.is_valid()) { new_image = _modulate_with_gradient(new_image, color_ramp); @@ -349,6 +353,18 @@ void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) { _queue_update(); } +void NoiseTexture2D::set_normalize(bool p_normalize) { + if (normalize == p_normalize) { + return; + } + normalize = p_normalize; + _queue_update(); +} + +bool NoiseTexture2D::is_normalized() const { + return normalize; +} + Ref<Gradient> NoiseTexture2D::get_color_ramp() const { return color_ramp; } diff --git a/modules/noise/noise_texture_2d.h b/modules/noise/noise_texture_2d.h index cda14df6c2..f53670b690 100644 --- a/modules/noise/noise_texture_2d.h +++ b/modules/noise/noise_texture_2d.h @@ -59,6 +59,7 @@ private: real_t seamless_blend_skirt = 0.1; bool as_normal_map = false; float bump_strength = 8.0; + bool normalize = true; Ref<Gradient> color_ramp; Ref<Noise> noise; @@ -105,6 +106,9 @@ public: void set_bump_strength(float p_bump_strength); float get_bump_strength(); + void set_normalize(bool p_normalize); + bool is_normalized() const; + void set_color_ramp(const Ref<Gradient> &p_gradient); Ref<Gradient> get_color_ramp() const; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index f80da6e9ab..2dcf7c3a11 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -652,6 +652,7 @@ Area2D::Area2D() : set_gravity_direction(Vector2(0, 1)); set_monitoring(true); set_monitorable(true); + set_hide_clip_children(true); } Area2D::~Area2D() { diff --git a/scene/2d/audio_listener_2d.cpp b/scene/2d/audio_listener_2d.cpp index 5b8833ce62..b4484694a5 100644 --- a/scene/2d/audio_listener_2d.cpp +++ b/scene/2d/audio_listener_2d.cpp @@ -110,3 +110,7 @@ void AudioListener2D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_current"), &AudioListener2D::clear_current); ClassDB::bind_method(D_METHOD("is_current"), &AudioListener2D::is_current); } + +AudioListener2D::AudioListener2D() { + set_hide_clip_children(true); +} diff --git a/scene/2d/audio_listener_2d.h b/scene/2d/audio_listener_2d.h index 12a44f26ae..abada06971 100644 --- a/scene/2d/audio_listener_2d.h +++ b/scene/2d/audio_listener_2d.h @@ -54,6 +54,8 @@ public: void make_current(); void clear_current(); bool is_current() const; + + AudioListener2D(); }; #endif // AUDIO_LISTENER_2D_H diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 8c23d33402..902fba38bf 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -486,6 +486,7 @@ void AudioStreamPlayer2D::_bind_methods() { AudioStreamPlayer2D::AudioStreamPlayer2D() { AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer2D::_bus_layout_changed)); cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength"); + set_hide_clip_children(true); } AudioStreamPlayer2D::~AudioStreamPlayer2D() { diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index ab048f0cd7..60b344b002 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -101,6 +101,7 @@ void BackBufferCopy::_bind_methods() { BackBufferCopy::BackBufferCopy() { _update_copy_mode(); + set_hide_clip_children(true); } BackBufferCopy::~BackBufferCopy() { diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index c4647ae9ff..71b8fdb539 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -832,4 +832,5 @@ Camera2D::Camera2D() { drag_margin[SIDE_BOTTOM] = 0.2; set_notify_transform(true); + set_hide_clip_children(true); } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index caea753d99..ba3b0cec5c 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -651,6 +651,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { area = p_area; pickable = true; set_notify_transform(true); + set_hide_clip_children(true); total_subshapes = 0; only_update_transform_changes = false; diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 0e18f77b8a..32dea80650 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -131,15 +131,7 @@ void CollisionPolygon2D::_notification(int p_what) { break; } - int polygon_count = polygon.size(); - for (int i = 0; i < polygon_count; i++) { - Vector2 p = polygon[i]; - Vector2 n = polygon[(i + 1) % polygon_count]; - // draw line with width <= 1, so it does not scale with zoom and break pixel exact editing - draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 1); - } - - if (polygon_count > 2) { + if (polygon.size() > 2) { #define DEBUG_DECOMPOSE #if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE) Vector<Vector<Vector2>> decomp = _decompose_in_convex(); @@ -152,6 +144,11 @@ void CollisionPolygon2D::_notification(int p_what) { #else draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color()); #endif + + const Color stroke_color = Color(0.9, 0.2, 0.0); + draw_polyline(polygon, stroke_color); + // Draw the last segment. + draw_line(polygon[polygon.size() - 1], polygon[0], stroke_color); } if (one_way_collision) { @@ -323,4 +320,5 @@ void CollisionPolygon2D::_bind_methods() { CollisionPolygon2D::CollisionPolygon2D() { set_notify_local_transform(true); + set_hide_clip_children(true); } diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 6ff789cad2..5951405bbe 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -288,5 +288,6 @@ void CollisionShape2D::_bind_methods() { CollisionShape2D::CollisionShape2D() { set_notify_local_transform(true); + set_hide_clip_children(true); debug_color = _get_default_debug_color(); } diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 47d0ac6e35..ce427d47aa 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -243,6 +243,7 @@ void Joint2D::_bind_methods() { Joint2D::Joint2D() { joint = PhysicsServer2D::get_singleton()->joint_create(); + set_hide_clip_children(true); } Joint2D::~Joint2D() { diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index fb53400fd6..15b638ed92 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -443,6 +443,7 @@ void PointLight2D::_bind_methods() { PointLight2D::PointLight2D() { RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_POINT); + set_hide_clip_children(true); } ////////// @@ -467,4 +468,5 @@ void DirectionalLight2D::_bind_methods() { DirectionalLight2D::DirectionalLight2D() { RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_DIRECTIONAL); set_max_distance(max_distance); // Update RenderingServer. + set_hide_clip_children(true); } diff --git a/scene/2d/marker_2d.cpp b/scene/2d/marker_2d.cpp index 512875833c..9595fcfffe 100644 --- a/scene/2d/marker_2d.cpp +++ b/scene/2d/marker_2d.cpp @@ -117,4 +117,5 @@ void Marker2D::_bind_methods() { } Marker2D::Marker2D() { + set_hide_clip_children(true); } diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index 65bf178cc5..26dca40176 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -308,6 +308,7 @@ NavigationLink2D::NavigationLink2D() { NavigationServer2D::get_singleton()->link_set_owner_id(link, get_instance_id()); set_notify_transform(true); + set_hide_clip_children(true); } NavigationLink2D::~NavigationLink2D() { diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index fe6af8dad2..3484a9de65 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -330,6 +330,7 @@ bool NavigationRegion2D::_get(const StringName &p_name, Variant &r_ret) const { NavigationRegion2D::NavigationRegion2D() { set_notify_transform(true); + set_hide_clip_children(true); region = NavigationServer2D::get_singleton()->region_create(); NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id()); diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 39f88a0b5e..988ea87054 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -370,4 +370,5 @@ void RayCast2D::_bind_methods() { } RayCast2D::RayCast2D() { + set_hide_clip_children(true); } diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp index e9ce9560d3..c6730f7ab2 100644 --- a/scene/2d/remote_transform_2d.cpp +++ b/scene/2d/remote_transform_2d.cpp @@ -219,4 +219,5 @@ void RemoteTransform2D::_bind_methods() { RemoteTransform2D::RemoteTransform2D() { set_notify_transform(true); + set_hide_clip_children(true); } diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index 24821858cf..bafb83361a 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -472,3 +472,7 @@ void ShapeCast2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled"); } + +ShapeCast2D::ShapeCast2D() { + set_hide_clip_children(true); +} diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h index 182614a721..8a62b799f8 100644 --- a/scene/2d/shape_cast_2d.h +++ b/scene/2d/shape_cast_2d.h @@ -119,6 +119,8 @@ public: void clear_exceptions(); PackedStringArray get_configuration_warnings() const override; + + ShapeCast2D(); }; #endif // SHAPE_CAST_2D_H diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 83cfffc333..96711bbe72 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -519,6 +519,7 @@ Bone2D::Bone2D() { bone_angle = 0; autocalculate_length_and_angle = true; set_notify_local_transform(true); + set_hide_clip_children(true); //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error. for (int i = 0; i < 3; i++) { rest[i] = Vector2(0, 0); @@ -801,6 +802,7 @@ void Skeleton2D::_bind_methods() { Skeleton2D::Skeleton2D() { skeleton = RS::get_singleton()->skeleton_create(); set_notify_transform(true); + set_hide_clip_children(true); } Skeleton2D::~Skeleton2D() { diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp index 237eb3d987..1177cdb811 100644 --- a/scene/2d/visible_on_screen_notifier_2d.cpp +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -110,6 +110,7 @@ void VisibleOnScreenNotifier2D::_bind_methods() { VisibleOnScreenNotifier2D::VisibleOnScreenNotifier2D() { rect = Rect2(-10, -10, 20, 20); + set_hide_clip_children(true); } ////////////////////////////////////// diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index c977d9d2fb..b084cb5bea 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -3088,6 +3088,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } code_completion_options.append_array(completion_options_casei); + code_completion_options.append_array(completion_options_substr); + code_completion_options.append_array(completion_options_substr_casei); code_completion_options.append_array(completion_options_subseq); code_completion_options.append_array(completion_options_subseq_casei); diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 3323c0f848..35176f0edd 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -917,6 +917,12 @@ void CanvasItem::force_update_transform() { notification(NOTIFICATION_TRANSFORM_CHANGED); } +void CanvasItem::_validate_property(PropertyInfo &p_property) const { + if (hide_clip_children && p_property.name == "clip_children") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_top_level_raise_self"), &CanvasItem::_top_level_raise_self); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 644fe856ec..1c84ea338a 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -106,6 +106,7 @@ private: bool use_parent_material = false; bool notify_local_transform = false; bool notify_transform = false; + bool hide_clip_children = false; ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED; @@ -155,6 +156,9 @@ protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + + _FORCE_INLINE_ void set_hide_clip_children(bool p_value) { hide_clip_children = p_value; } GDVIRTUAL0(_draw) public: diff --git a/scene/main/node.cpp b/scene/main/node.cpp index de486094fe..ba75c92c85 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1987,7 +1987,16 @@ String Node::get_scene_file_path() const { } void Node::set_editor_description(const String &p_editor_description) { + if (data.editor_description == p_editor_description) { + return; + } + data.editor_description = p_editor_description; + + if (Engine::get_singleton()->is_editor_hint() && is_inside_tree()) { + // Update tree so the tooltip in the Scene tree dock is also updated in the editor. + get_tree()->tree_changed(); + } } String Node::get_editor_description() const { diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 5dc4e64c2e..5309e54846 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -88,8 +88,10 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector<Vector2> points = _get_points(); Vector<Color> col = { p_color }; RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); + if (is_collision_outline_enabled()) { points.push_back(points[0]); + col = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); } } diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index a15dfc0a54..0b207c33ca 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -84,6 +84,7 @@ void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) { if (is_collision_outline_enabled()) { points.push_back(points[0]); + col = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); } } diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index e51b48a4b1..7f19dd63e6 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -76,13 +76,14 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) { return; } - Vector<Color> col; - col.push_back(p_color); + Vector<Color> col = { p_color }; RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); + if (is_collision_outline_enabled()) { + col = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); - // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0); + // Draw the last segment. + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], Color(p_color, 1.0)); } } diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 84c147b4b4..65b1653293 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -79,11 +79,7 @@ void RectangleShape2D::draw(const RID &p_to_rid, const Color &p_color) { stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5; stroke_points.write[4] = -size * 0.5; - Vector<Color> stroke_colors; - stroke_colors.resize(5); - for (int i = 0; i < 5; i++) { - stroke_colors.write[i] = (p_color); - } + Vector<Color> stroke_colors = { Color(p_color, 1.0) }; RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors); } diff --git a/scene/resources/world_boundary_shape_2d.cpp b/scene/resources/world_boundary_shape_2d.cpp index 49f0873a3e..35cb8ef13d 100644 --- a/scene/resources/world_boundary_shape_2d.cpp +++ b/scene/resources/world_boundary_shape_2d.cpp @@ -76,11 +76,12 @@ real_t WorldBoundaryShape2D::get_distance() const { void WorldBoundaryShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector2 point = get_distance() * get_normal(); + real_t line_width = 3.0; Vector2 l1[2] = { point - get_normal().orthogonal() * 100, point + get_normal().orthogonal() * 100 }; - RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, 3); - Vector2 l2[2] = { point, point + get_normal() * 30 }; - RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, 3); + RS::get_singleton()->canvas_item_add_line(p_to_rid, l1[0], l1[1], p_color, line_width); + Vector2 l2[2] = { point + get_normal().normalized() * (0.5 * line_width), point + get_normal() * 30 }; + RS::get_singleton()->canvas_item_add_line(p_to_rid, l2[0], l2[1], p_color, line_width); } Rect2 WorldBoundaryShape2D::get_rect() const { diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 4ef562adcb..97907bed48 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2875,7 +2875,7 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/2d/shadow_atlas/size", 2048); // Number of commands that can be drawn per frame. - GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "1024,1048576,1"), 16384); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/gl_compatibility/item_buffer_size", PROPERTY_HINT_RANGE, "128,1048576,1"), 16384); GLOBAL_DEF("rendering/shader_compiler/shader_cache/enabled", true); GLOBAL_DEF("rendering/shader_compiler/shader_cache/compress", true); diff --git a/tests/core/input/test_input_event_mouse.h b/tests/core/input/test_input_event_mouse.h new file mode 100644 index 0000000000..0da4f14160 --- /dev/null +++ b/tests/core/input/test_input_event_mouse.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* test_input_event_mouse.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_INPUT_EVENT_MOUSE_H +#define TEST_INPUT_EVENT_MOUSE_H + +#include "core/input/input_event.h" +#include "tests/test_macros.h" + +namespace TestInputEventMouse { + +TEST_CASE("[InputEventMouse] Mouse button mask is set correctly") { + InputEventMouse mousekey; + + mousekey.set_button_mask(MouseButtonMask::LEFT); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::LEFT)); + + mousekey.set_button_mask(MouseButtonMask::MB_XBUTTON1); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MB_XBUTTON1)); + + mousekey.set_button_mask(MouseButtonMask::MB_XBUTTON2); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MB_XBUTTON2)); + + mousekey.set_button_mask(MouseButtonMask::MIDDLE); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MIDDLE)); + + mousekey.set_button_mask(MouseButtonMask::RIGHT); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::RIGHT)); +} + +TEST_CASE("[InputEventMouse] Setting the mouse position works correctly") { + InputEventMouse mousekey; + + mousekey.set_position(Vector2{ 10, 10 }); + CHECK(mousekey.get_position() == Vector2{ 10, 10 }); + + mousekey.set_position(Vector2{ -1, -1 }); + CHECK(mousekey.get_position() == Vector2{ -1, -1 }); +} + +TEST_CASE("[InputEventMouse] Setting the global mouse position works correctly") { + InputEventMouse mousekey; + + mousekey.set_global_position(Vector2{ 10, 10 }); + CHECK(mousekey.get_global_position() == Vector2{ 10, 10 }); + CHECK(mousekey.get_global_position() != Vector2{ 1, 1 }); + + mousekey.set_global_position(Vector2{ -1, -1 }); + CHECK(mousekey.get_global_position() == Vector2{ -1, -1 }); + CHECK(mousekey.get_global_position() != Vector2{ 1, 1 }); +} +} // namespace TestInputEventMouse + +#endif // TEST_INPUT_EVENT_MOUSE_H diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h index dc2b6e2ba8..ca27776180 100644 --- a/tests/core/math/test_transform_2d.h +++ b/tests/core/math/test_transform_2d.h @@ -84,6 +84,19 @@ TEST_CASE("[Transform2D] rotation") { CHECK(orig.rotated_local(phi) == orig * R); } +TEST_CASE("[Transform2D] Interpolation") { + Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8)); + Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4)); + Transform2D interpolated = Transform2D().interpolate_with(rotate_scale_skew_pos, 0.5); + CHECK(interpolated.get_origin().is_equal_approx(rotate_scale_skew_pos_halfway.get_origin())); + CHECK(interpolated.get_rotation() == doctest::Approx(rotate_scale_skew_pos_halfway.get_rotation())); + CHECK(interpolated.get_scale().is_equal_approx(rotate_scale_skew_pos_halfway.get_scale())); + CHECK(interpolated.get_skew() == doctest::Approx(rotate_scale_skew_pos_halfway.get_skew())); + CHECK(interpolated.is_equal_approx(rotate_scale_skew_pos_halfway)); + interpolated = rotate_scale_skew_pos.interpolate_with(Transform2D(), 0.5); + CHECK(interpolated.is_equal_approx(rotate_scale_skew_pos_halfway)); +} + TEST_CASE("[Transform2D] Finite number checks") { const Vector2 x(0, 1); const Vector2 infinite(NAN, NAN); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 8c563f94ac..c7f2d4cbfb 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -32,6 +32,7 @@ #include "tests/core/config/test_project_settings.h" #include "tests/core/input/test_input_event_key.h" +#include "tests/core/input/test_input_event_mouse.h" #include "tests/core/input/test_shortcut.h" #include "tests/core/io/test_config_file.h" #include "tests/core/io/test_file_access.h" |