From 147b4bf7a9bc37bcf2bf3181896a3452508adf0f Mon Sep 17 00:00:00 2001 From: LuoZhihao Date: Wed, 29 Jan 2025 17:54:24 +0800 Subject: [PATCH] Refactor MeshTexture 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 --- doc/classes/MeshTexture.xml | 15 ++- scene/resources/mesh_texture.cpp | 170 ++++++++++++++++++++----------- scene/resources/mesh_texture.h | 32 ++++-- 3 files changed, 150 insertions(+), 67 deletions(-) diff --git a/doc/classes/MeshTexture.xml b/doc/classes/MeshTexture.xml index bea99a4d5c54..bb0c1944ece1 100644 --- a/doc/classes/MeshTexture.xml +++ b/doc/classes/MeshTexture.xml @@ -1,23 +1,32 @@ - Simple texture that uses a mesh to draw itself. + A 2D texture that uses a mesh to draw itself. - 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. + + Sets the background color. + Sets the base texture that the Mesh will use to draw. - + Sets the size of the image, needed for reference. Sets the mesh used to draw. It must be a mesh using 2D vertices. + + Sets the offset for mesh drawing in pixels. + + + Sets the scale for mesh drawing. + diff --git a/scene/resources/mesh_texture.cpp b/scene/resources/mesh_texture.cpp index 1440b7f02b2d..d2dd4e9c12a9 100644 --- a/scene/resources/mesh_texture.cpp +++ b/scene/resources/mesh_texture.cpp @@ -41,15 +41,42 @@ 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 img = get_image(); + if (img.is_null()) { + return true; + } + return img->get_pixel(p_x, p_y).a != 0; +} + +Ref 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 &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 MeshTexture::get_mesh() const { @@ -57,7 +84,10 @@ Ref MeshTexture::get_mesh() const { } 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 { @@ -65,78 +95,71 @@ Size2 MeshTexture::get_image_size() const { } void MeshTexture::set_base_texture(const Ref &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 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() { @@ -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); } diff --git a/scene/resources/mesh_texture.h b/scene/resources/mesh_texture.h index 35ccb727c4a8..8754b4591ad8 100644 --- a/scene/resources/mesh_texture.h +++ b/scene/resources/mesh_texture.h @@ -41,7 +41,18 @@ class MeshTexture : public Texture2D { Ref base_texture; Ref 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(); @@ -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 get_image() const override; + + virtual void set_path(const String &p_path, bool p_take_over = false) override; + void set_mesh(const Ref &p_mesh); Ref get_mesh() const; @@ -62,14 +79,17 @@ class MeshTexture : public Texture2D { void set_base_texture(const Ref &p_texture); Ref 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