Skip to content

Commit

Permalink
Add ShapeTexture2D
Browse files Browse the repository at this point in the history
Apply suggestions from code review
  • Loading branch information
aXu-AP committed Nov 7, 2023
1 parent 3e7f638 commit ee5567c
Show file tree
Hide file tree
Showing 4 changed files with 441 additions and 0 deletions.
46 changes: 46 additions & 0 deletions doc/classes/ShapeTexture2D.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ShapeTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A 2D texture of a regular shape.
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="is_using_hdr" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code], if the texture is HDR texture. HDR is enabled automatically if [member fill_color] or [member border_color] is overbright.
</description>
</method>
</methods>
<members>
<member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="false">
If enabled, the edges of the shape appear smoother.
</member>
<member name="border_color" type="Color" setter="set_border_color" getter="get_border_color" default="Color(0, 0, 0, 1)">
</member>
<member name="border_width" type="float" setter="set_border_width" getter="get_border_width" default="0.0">
</member>
<member name="fill_color" type="Color" setter="set_fill_color" getter="get_fill_color" default="Color(1, 1, 1, 1)">
</member>
<member name="height" type="int" setter="set_height" getter="get_height" default="64">
</member>
<member name="points" type="int" setter="set_points" getter="get_points" default="3">
Amount of points. For example [code]3[/code] for triangle, [code]4[/code] for rectangle, big number for circle.
</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="rotation_degrees" type="float" setter="set_rotation_degrees" getter="get_rotation_degrees" default="0.0">
Rotates the shape.
</member>
<member name="star_enabled" type="bool" setter="set_star_enabled" getter="is_star_enabled" default="false">
If enabled, adds additional vertices between each vertex for a star shape.
</member>
<member name="star_inset" type="float" setter="set_star_inset" getter="get_star_inset" default="0.5">
How far towards the center inner vertices go if [member star_enabled] is enabled. 0 = no insetting, 1 = center.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="64">
</member>
</members>
</class>
2 changes: 2 additions & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down
297 changes: 297 additions & 0 deletions scene/resources/shape_texture_2d.cpp
Original file line number Diff line number Diff line change
@@ -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;
image.instantiate();

Vector<Vector2> fill_polygon;
Vector<Vector2> 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<uint8_t> 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<Image> ShapeTexture2D::get_image() const {
const_cast<ShapeTexture2D *>(this)->update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}

void ShapeTexture2D::update_now() {
if (update_pending) {
_update();
}
}
Loading

0 comments on commit ee5567c

Please sign in to comment.