Skip to content

Commit

Permalink
Refactor MeshTexture
Browse files Browse the repository at this point in the history
Fixes properties changing issue. Sets the mesh drawing origin to the center and scales the mesh to fit texture size. Adds some properties for adjustment
  • Loading branch information
beicause committed Jan 29, 2025
1 parent 55fb3bc commit 147b4bf
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 67 deletions.
15 changes: 12 additions & 3 deletions doc/classes/MeshTexture.xml
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="MeshTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Simple texture that uses a mesh to draw itself.
A 2D texture that uses a mesh to draw itself.
</brief_description>
<description>
Simple texture that uses a mesh to draw itself. It's limited because flags can't be changed and region drawing is not supported.
A 2D texture that uses a mesh with a base texture to draw itself. Internally it is drawn using [method RenderingServer.canvas_item_add_mesh] and [Viewport] to capture.
</description>
<tutorials>
</tutorials>
<members>
<member name="background" type="Color" setter="set_background" getter="get_background" default="Color(0, 0, 0, 0)">
Sets the background color.
</member>
<member name="base_texture" type="Texture2D" setter="set_base_texture" getter="get_base_texture">
Sets the base texture that the Mesh will use to draw.
</member>
<member name="image_size" type="Vector2" setter="set_image_size" getter="get_image_size" default="Vector2(0, 0)">
<member name="image_size" type="Vector2" setter="set_image_size" getter="get_image_size" default="Vector2(512, 512)">
Sets the size of the image, needed for reference.
</member>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
Sets the mesh used to draw. It must be a mesh using 2D vertices.
</member>
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
Sets the offset for mesh drawing in pixels.
</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="scale" type="Vector2" setter="set_scale" getter="get_scale" default="Vector2(1, 1)">
Sets the scale for mesh drawing.
</member>
</members>
</class>
170 changes: 112 additions & 58 deletions scene/resources/mesh_texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,102 +41,125 @@ int MeshTexture::get_height() const {
}

RID MeshTexture::get_rid() const {
return RID();
return texture;
}

bool MeshTexture::has_alpha() const {
return false;
return true;
}

bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const {
Ref<Image> img = get_image();
if (img.is_null()) {
return true;
}
return img->get_pixel(p_x, p_y).a != 0;
}

Ref<Image> MeshTexture::get_image() const {
return RS::get_singleton()->texture_2d_get(texture);
}

void MeshTexture::set_path(const String &p_path, bool p_take_over) {
if (texture.is_valid()) {
RS::get_singleton()->texture_set_path(texture, p_path);
}

Resource::set_path(p_path, p_take_over);
}

void MeshTexture::set_mesh(const Ref<Mesh> &p_mesh) {
if (mesh.is_valid()) {
mesh->disconnect_changed(callable_mp(this, &MeshTexture::_queue_update));
}
mesh = p_mesh;
if (mesh.is_valid()) {
mesh->connect_changed(callable_mp(this, &MeshTexture::_queue_update));
}
_queue_update();
}

Ref<Mesh> MeshTexture::get_mesh() const {
return mesh;
}

void MeshTexture::set_image_size(const Size2 &p_size) {
ERR_FAIL_COND(p_size.width <= 0 || p_size.height <= 0);
size = p_size;
RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
_queue_update();
}

Size2 MeshTexture::get_image_size() const {
return size;
}

void MeshTexture::set_base_texture(const Ref<Texture2D> &p_texture) {
if (base_texture.is_valid()) {
base_texture->disconnect_changed(callable_mp(this, &MeshTexture::_queue_update));
}
base_texture = p_texture;
if (base_texture.is_valid()) {
base_texture->connect_changed(callable_mp(this, &MeshTexture::_queue_update));
}
_queue_update();
}

Ref<Texture2D> MeshTexture::get_base_texture() const {
return base_texture;
}

void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
Transform2D xform;
xform.set_origin(p_pos);
if (p_transpose) {
SWAP(xform.columns[0][1], xform.columns[1][0]);
SWAP(xform.columns[0][0], xform.columns[1][1]);
}
RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
void MeshTexture::set_scale(const Size2 &p_scale) {
scale = p_scale;
_queue_update();
}

void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
Transform2D xform;
Vector2 origin = p_rect.position;
if (p_rect.size.x < 0) {
origin.x += size.x;
}
if (p_rect.size.y < 0) {
origin.y += size.y;
}
xform.set_origin(origin);
xform.set_scale(p_rect.size / size);
Size2 MeshTexture::get_scale() const {
return scale;
}

if (p_transpose) {
SWAP(xform.columns[0][1], xform.columns[1][0]);
SWAP(xform.columns[0][0], xform.columns[1][1]);
}
RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
void MeshTexture::set_offset(const Size2 &p_offset) {
offset = p_offset;
_queue_update();
}

void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
if (mesh.is_null() || base_texture.is_null()) {
return;
}
Transform2D xform;
Vector2 origin = p_rect.position;
if (p_rect.size.x < 0) {
origin.x += size.x;
}
if (p_rect.size.y < 0) {
origin.y += size.y;
}
xform.set_origin(origin);
xform.set_scale(p_rect.size / size);
Size2 MeshTexture::get_offset() const {
return offset;
}

if (p_transpose) {
SWAP(xform.columns[0][1], xform.columns[1][0]);
SWAP(xform.columns[0][0], xform.columns[1][1]);
}
RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
void MeshTexture::set_background(const Color &p_color) {
background = p_color;
_queue_update();
}

bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
r_rect = p_rect;
r_src_rect = p_src_rect;
return true;
Color MeshTexture::get_background() const {
return background;
}

bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const {
return true;
void MeshTexture::_queue_update() {
if (update_pending) {
return;
}
update_pending = true;
callable_mp(this, &MeshTexture::_update_viewport_texture).call_deferred();
}

void MeshTexture::_update_viewport_texture() {
RS::get_singleton()->canvas_item_clear(canvas_item);
RS::get_singleton()->canvas_item_add_rect(canvas_item, Rect2(Vector2(), size), background);
if (mesh.is_valid() && base_texture.is_valid()) {
Transform2D xform;
Vector3 mesh_size = mesh->get_aabb().get_size();
float mesh_scale = MAX(mesh_size.x, mesh_size.y);
xform.set_scale(size / mesh_scale * scale);
xform.set_origin(size / 2 + offset);
RS::get_singleton()->canvas_item_add_mesh(canvas_item, mesh->get_rid(), xform, Color(1, 1, 1), base_texture->get_rid());
}
RS::get_singleton()->viewport_set_active(viewport, true);
RS::get_singleton()->draw(false);
RS::get_singleton()->viewport_set_active(viewport, false);
emit_changed();
update_pending = false;
}

void MeshTexture::_bind_methods() {
Expand All @@ -146,11 +169,42 @@ void MeshTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_image_size"), &MeshTexture::get_image_size);
ClassDB::bind_method(D_METHOD("set_base_texture", "texture"), &MeshTexture::set_base_texture);
ClassDB::bind_method(D_METHOD("get_base_texture"), &MeshTexture::get_base_texture);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &MeshTexture::set_scale);
ClassDB::bind_method(D_METHOD("get_scale"), &MeshTexture::get_scale);
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &MeshTexture::set_offset);
ClassDB::bind_method(D_METHOD("get_offset"), &MeshTexture::get_offset);
ClassDB::bind_method(D_METHOD("set_background", "enable"), &MeshTexture::set_background);
ClassDB::bind_method(D_METHOD("get_background"), &MeshTexture::get_background);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_base_texture", "get_base_texture");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_image_size", "get_image_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,hide_slider,suffix:px"), "set_image_size", "get_image_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_RANGE, "-1,1,0.01,or_greater,or_less,hide_slider"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_RANGE, "-512,512,0.1,or_greater,or_less,hide_slider,suffix:px"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background"), "set_background", "get_background");
}

MeshTexture::MeshTexture() {
canvas_item = RS::get_singleton()->canvas_item_create();
canvas = RS::get_singleton()->canvas_create();
viewport = RS::get_singleton()->viewport_create();
RS::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
RS::get_singleton()->viewport_set_disable_3d(viewport, true);
RS::get_singleton()->viewport_attach_canvas(viewport, canvas);
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ALWAYS);
RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);

RS::get_singleton()->canvas_item_add_rect(canvas_item, Rect2(Vector2(), size), background);
RS::get_singleton()->viewport_set_active(viewport, true);
RS::get_singleton()->draw(false);
RS::get_singleton()->viewport_set_active(viewport, false);

texture = RS::get_singleton()->viewport_get_texture(viewport);
}

MeshTexture::~MeshTexture() {
RS::get_singleton()->free(viewport);
RS::get_singleton()->free(canvas);
RS::get_singleton()->free(canvas_item);
}
32 changes: 26 additions & 6 deletions scene/resources/mesh_texture.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,18 @@ class MeshTexture : public Texture2D {

Ref<Texture2D> base_texture;
Ref<Mesh> mesh;
Size2i size;
RID texture;
RID viewport;
RID canvas;
RID canvas_item;
Size2 size = Size2(512, 512);
Size2 scale = Size2(1, 1);
Size2 offset = Size2(0, 0);
Color background = Color(0, 0, 0, 0);

bool update_pending = false;
void _queue_update();
void _update_viewport_texture();

protected:
static void _bind_methods();
Expand All @@ -53,6 +64,12 @@ class MeshTexture : public Texture2D {

virtual bool has_alpha() const override;

virtual bool is_pixel_opaque(int p_x, int p_y) const override;

virtual Ref<Image> get_image() const override;

virtual void set_path(const String &p_path, bool p_take_over = false) override;

void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh() const;

Expand All @@ -62,14 +79,17 @@ class MeshTexture : public Texture2D {
void set_base_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_base_texture() const;

virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
void set_scale(const Size2 &p_scale);
Size2 get_scale() const;

void set_offset(const Size2 &p_offset);
Size2 get_offset() const;

bool is_pixel_opaque(int p_x, int p_y) const override;
void set_background(const Color &p_color);
Color get_background() const;

MeshTexture();
~MeshTexture();
};

#endif // MESH_TEXTURE_H

0 comments on commit 147b4bf

Please sign in to comment.