diff --git a/doc/classes/ShapeTexture2D.xml b/doc/classes/ShapeTexture2D.xml new file mode 100644 index 000000000000..902fcbd93398 --- /dev/null +++ b/doc/classes/ShapeTexture2D.xml @@ -0,0 +1,46 @@ + + + + A 2D texture of a regular shape. + + + + + + + + + + Returns [code]true[/code], if the texture is HDR texture. HDR is enabled automatically if [member fill_color] or [member border_color] is overbright. + + + + + + If enabled, the edges of the shape appear smoother. + + + + + + + + + + + Amount of points. For example [code]3[/code] for triangle, [code]4[/code] for rectangle, big number for circle. + + + + Rotates the shape. + + + If enabled, adds additional vertices between each vertex for a star shape. + + + How far towards the center inner vertices go if [member star_enabled] is enabled. 0 = no insetting, 1 = center. + + + + + diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index b7c98b0ea9a6..cae7a647aa4d 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -189,6 +189,7 @@ #include "scene/resources/separation_ray_shape_2d.h" #include "scene/resources/separation_ray_shape_3d.h" #include "scene/resources/shader_include.h" +#include "scene/resources/shape_texture_2d.h" #include "scene/resources/skeleton_modification_2d.h" #include "scene/resources/skeleton_modification_2d_ccdik.h" #include "scene/resources/skeleton_modification_2d_fabrik.h" @@ -872,6 +873,7 @@ void register_scene_types() { GDREGISTER_CLASS(CurveXYZTexture); GDREGISTER_CLASS(GradientTexture1D); GDREGISTER_CLASS(GradientTexture2D); + GDREGISTER_CLASS(ShapeTexture2D); GDREGISTER_CLASS(AnimatedTexture); GDREGISTER_CLASS(CameraTexture); GDREGISTER_VIRTUAL_CLASS(TextureLayered); diff --git a/scene/resources/shape_texture_2d.cpp b/scene/resources/shape_texture_2d.cpp new file mode 100644 index 000000000000..20395674c001 --- /dev/null +++ b/scene/resources/shape_texture_2d.cpp @@ -0,0 +1,297 @@ +/**************************************************************************/ +/* shape_texture_2d.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "shape_texture_2d.h" + +#include "core/core_string_names.h" +#include "core/math/color.h" +#include "core/math/geometry_2d.h" + +ShapeTexture2D::ShapeTexture2D() { + _queue_update(); +} + +ShapeTexture2D::~ShapeTexture2D() { + if (texture.is_valid()) { + ERR_FAIL_NULL(RenderingServer::get_singleton()); + RS::get_singleton()->free(texture); + } +} + +void ShapeTexture2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_width", "width"), &ShapeTexture2D::set_width); + ClassDB::bind_method(D_METHOD("set_height", "height"), &ShapeTexture2D::set_height); + // `get_width()` and `get_height()` methods are already exposed by the parent class Texture2D. + ClassDB::bind_method(D_METHOD("set_points", "points"), &ShapeTexture2D::set_points); + ClassDB::bind_method(D_METHOD("get_points"), &ShapeTexture2D::get_points); + ClassDB::bind_method(D_METHOD("set_star_enabled", "star_enabled"), &ShapeTexture2D::set_star_enabled); + ClassDB::bind_method(D_METHOD("is_star_enabled"), &ShapeTexture2D::is_star_enabled); + ClassDB::bind_method(D_METHOD("set_star_inset", "star_inset"), &ShapeTexture2D::set_star_inset); + ClassDB::bind_method(D_METHOD("get_star_inset"), &ShapeTexture2D::get_star_inset); + ClassDB::bind_method(D_METHOD("set_rotation_degrees", "rotation_degrees"), &ShapeTexture2D::set_rotation_degrees); + ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &ShapeTexture2D::get_rotation_degrees); + ClassDB::bind_method(D_METHOD("set_fill_color", "fill_color"), &ShapeTexture2D::set_fill_color); + ClassDB::bind_method(D_METHOD("get_fill_color"), &ShapeTexture2D::get_fill_color); + ClassDB::bind_method(D_METHOD("set_border_color", "border_color"), &ShapeTexture2D::set_border_color); + ClassDB::bind_method(D_METHOD("get_border_color"), &ShapeTexture2D::get_border_color); + ClassDB::bind_method(D_METHOD("set_border_width", "border_width"), &ShapeTexture2D::set_border_width); + ClassDB::bind_method(D_METHOD("get_border_width"), &ShapeTexture2D::get_border_width); + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &ShapeTexture2D::set_antialiased); + ClassDB::bind_method(D_METHOD("is_antialiased"), &ShapeTexture2D::is_antialiased); + + ClassDB::bind_method(D_METHOD("is_using_hdr"), &ShapeTexture2D::is_using_hdr); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "points", PROPERTY_HINT_RANGE, "3,100,or_greater"), "set_points", "get_points"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "star_enabled"), "set_star_enabled", "is_star_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "star_inset", PROPERTY_HINT_RANGE, "0,1"), "set_star_inset", "get_star_inset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_RANGE, "-180,180"), "set_rotation_degrees", "get_rotation_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fill_color"), "set_fill_color", "get_fill_color"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "border_width", PROPERTY_HINT_RANGE, "0,50,or_greater"), "set_border_width", "get_border_width"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased"); +} + +void ShapeTexture2D::_queue_update() { + if (update_pending) { + return; + } + update_pending = true; + callable_mp(this, &ShapeTexture2D::update_now).call_deferred(); +} + +void ShapeTexture2D::_update() { + update_pending = false; + + Ref image; + image.instantiate(); + + Vector fill_polygon; + Vector border_polygon; + int total_points = star_enabled ? points * 2 : points; + border_polygon.resize(total_points); + fill_polygon.resize(total_points); + Vector2 offset = Vector2(.5, .5); + Vector2 border_point = Vector2(0, -.5); + real_t fill_inset = border_width / height; + Vector2 fill_point = Vector2(0, -.5 + fill_inset); + real_t angle_delta = Math_TAU / total_points; + real_t initial_angle = Math::deg_to_rad(rotation_degrees); + Vector2 scale = Vector2(width, height); + for (int i = 0; i < total_points; i++) { + real_t inset = star_enabled && i % 2 ? 1 - star_inset : 1; + border_polygon.write[i] = (offset + border_point.rotated(initial_angle + i * angle_delta) * inset) * scale; + fill_polygon.write[i] = (offset + fill_point.rotated(initial_angle + i * angle_delta) * inset) * scale; + } + + if (is_using_hdr()) { + image->initialize_data(width, height, false, Image::FORMAT_RGBAF); + // `create()` isn't available for non-uint8_t data, so fill in the data manually. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + image->set_pixel(x, y, _get_color(Vector2(x, y), border_polygon, fill_polygon, antialiased)); + } + } + } else { + Vector data; + data.resize(width * height * 4); + { + uint8_t *wd8 = data.ptrw(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const Color &c = _get_color(Vector2(x, y), border_polygon, fill_polygon, antialiased); + + wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(c.r * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(c.g * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(c.b * 255.0, 0, 255)); + wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(c.a * 255.0, 0, 255)); + } + } + } + image->set_data(width, height, false, Image::FORMAT_RGBA8, data); + } + + if (texture.is_valid()) { + RID new_texture = RS::get_singleton()->texture_2d_create(image); + RS::get_singleton()->texture_replace(texture, new_texture); + } else { + texture = RS::get_singleton()->texture_2d_create(image); + } +} + +Color ShapeTexture2D::_get_color(Vector2 p_point, PackedVector2Array &p_border, PackedVector2Array &p_fill, bool p_supersample) { + if (p_supersample) { + return (_get_color(p_point, p_border, p_fill, false) + + _get_color(p_point + Vector2(0.5, 0), p_border, p_fill, false) + + _get_color(p_point + Vector2(0, 0.5), p_border, p_fill, false) + + _get_color(p_point + Vector2(0.5, 0.5), p_border, p_fill, false)) / + 4; + } else { + if (Geometry2D::is_point_in_polygon(p_point, p_border)) { + if (Geometry2D::is_point_in_polygon(p_point, p_fill)) { + return fill_color; + } else { + return border_color; + } + } else { + return background_color; + } + } +} + +void ShapeTexture2D::set_width(int p_width) { + ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range."); + width = p_width; + _queue_update(); + emit_changed(); +} + +int ShapeTexture2D::get_width() const { + return width; +} + +void ShapeTexture2D::set_height(int p_height) { + ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be within 1 to 16384 range."); + height = p_height; + _queue_update(); + emit_changed(); +} + +int ShapeTexture2D::get_height() const { + return height; +} + +void ShapeTexture2D::set_points(int p_points) { + points = p_points; + _queue_update(); + emit_changed(); +} + +int ShapeTexture2D::get_points() const { + return points; +} + +void ShapeTexture2D::set_star_enabled(bool p_is_star) { + star_enabled = p_is_star; + _queue_update(); + emit_changed(); +} + +bool ShapeTexture2D::is_star_enabled() const { + return star_enabled; +} + +void ShapeTexture2D::set_star_inset(real_t p_star_inset) { + star_inset = p_star_inset; + _queue_update(); + emit_changed(); +} + +real_t ShapeTexture2D::get_star_inset() const { + return star_inset; +} + +void ShapeTexture2D::set_rotation_degrees(real_t p_rotation_degrees) { + rotation_degrees = p_rotation_degrees; + _queue_update(); + emit_changed(); +} + +real_t ShapeTexture2D::get_rotation_degrees() const { + return rotation_degrees; +} + +void ShapeTexture2D::set_fill_color(Color p_fill_color) { + fill_color = p_fill_color; + _queue_update(); + emit_changed(); +} + +Color ShapeTexture2D::get_fill_color() const { + return fill_color; +} + +void ShapeTexture2D::set_border_color(Color p_border_color) { + border_color = p_border_color; + background_color = p_border_color; + background_color.a = 0; + _queue_update(); + emit_changed(); +} + +Color ShapeTexture2D::get_border_color() const { + return border_color; +} + +void ShapeTexture2D::set_border_width(real_t p_border_width) { + border_width = p_border_width; + _queue_update(); + emit_changed(); +} + +real_t ShapeTexture2D::get_border_width() const { + return border_width; +} + +void ShapeTexture2D::set_antialiased(bool p_antialiased) { + antialiased = p_antialiased; + _queue_update(); + emit_changed(); +} + +bool ShapeTexture2D::is_antialiased() const { + return antialiased; +} + +bool ShapeTexture2D::is_using_hdr() const { + return fill_color.r > 1 || fill_color.g > 1 || fill_color.b > 1 || border_color.r > 1 || border_color.g > 1 || border_color.b > 1; +} + +RID ShapeTexture2D::get_rid() const { + if (!texture.is_valid()) { + texture = RS::get_singleton()->texture_2d_placeholder_create(); + } + return texture; +} + +Ref ShapeTexture2D::get_image() const { + const_cast(this)->update_now(); + if (!texture.is_valid()) { + return Ref(); + } + return RenderingServer::get_singleton()->texture_2d_get(texture); +} + +void ShapeTexture2D::update_now() { + if (update_pending) { + _update(); + } +} diff --git a/scene/resources/shape_texture_2d.h b/scene/resources/shape_texture_2d.h new file mode 100644 index 000000000000..addf523ec57b --- /dev/null +++ b/scene/resources/shape_texture_2d.h @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* shape_texture_2d.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 SHAPE_TEXTURE_2D_H +#define SHAPE_TEXTURE_2D_H + +#include "scene/resources/texture.h" + +class ShapeTexture2D : public Texture2D { + GDCLASS(ShapeTexture2D, Texture2D); + +private: + int width = 64; + int height = 64; + int points = 3; + bool star_enabled = false; + real_t star_inset = 0.5; + real_t rotation_degrees = 0.0; + Color fill_color = Color(1, 1, 1, 1); + Color border_color; + real_t border_width = 0.0; + bool antialiased = false; + + bool update_pending = false; + mutable RID texture; + Color background_color = Color(0, 0, 0, 0); + + void _queue_update(); + void _update(); + Color _get_color(Vector2 p_point, PackedVector2Array &p_border, PackedVector2Array &p_fill, bool p_supersample); + +protected: + static void _bind_methods(); + +public: + void set_width(int p_width); + int get_width() const override; + void set_height(int p_height); + int get_height() const override; + void set_points(int p_points); + int get_points() const; + void set_star_enabled(bool p_is_star); + bool is_star_enabled() const; + void set_star_inset(real_t p_star_inset); + real_t get_star_inset() const; + void set_rotation_degrees(real_t p_rotation_degrees); + real_t get_rotation_degrees() const; + void set_fill_color(Color p_fill_color); + Color get_fill_color() const; + void set_border_color(Color p_border_color); + Color get_border_color() const; + void set_border_width(real_t p_border_width); + real_t get_border_width() const; + void set_antialiased(bool p_antialiased); + bool is_antialiased() const; + + bool is_using_hdr() const; + + virtual RID get_rid() const override; + virtual bool has_alpha() const override { return true; } + + virtual Ref get_image() const override; + void update_now(); + + ShapeTexture2D(); + virtual ~ShapeTexture2D(); +}; + +#endif // SHAPE_TEXTURE_2D_H