Repository: cloudofoz/godot-deformablemesh
Branch: main
Commit: ee6f41486b87
Files: 21
Total size: 39.8 KB
Directory structure:
gitextract_20c1fllx/
├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
└── addons/
└── deformablemesh/
├── dm_debug_sphere_material.tres
├── dm_debug_sphere_mesh.tres
├── dm_deformable_mesh.gd
├── dm_deformable_mesh.gd.uid
├── dm_deformer.gd
├── dm_deformer.gd.uid
├── dm_drag_deformer.gd
├── dm_drag_deformer.gd.uid
├── dm_spherical_deformer.gd
├── dm_spherical_deformer.gd.uid
├── dm_std_deformer.gd
├── dm_std_deformer.gd.uid
├── dm_surface_data.gd
├── dm_surface_data.gd.uid
├── plugin.cfg
├── plugin.gd
└── plugin.gd.uid
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
# Only include the addons folder when downloading from the Asset Library.
/** export-ignore
/addons !export-ignore
/addons/** !export-ignore
================================================
FILE: .gitignore
================================================
# Godot 4+ specific ignores
.godot/
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (C) 2023 Claudio Z. (cloudofoz)
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.
================================================
FILE: README.md
================================================
 
<img src="addons/deformablemesh/dm_icon_deformable_mesh.svg" width="64" align="left"/>
## godot-deformablemesh
**This addon allows to deform 3D meshes using a stack of customizable deformers at run-time**
<br clear="left" />
<p align="center">
<img src="media/dm_screen_v04_1.gif" height="270" />
<img src="media/dm_screen_v03_1.gif" height="270" />
<img src="media/dm_example_scene_scr.jpg" height="270" />
</p>
## Main Features
Use the default deformers:
- `SphericalDeformer` <img src="addons/deformablemesh/dm_icon_spherical_deformer.svg" width="20"/>
- `StandardDeformer` <img src="addons/deformablemesh/dm_icon_std_deformer.svg" width="20"/> (Bend, Twist, and Taper)
- `DragDeformer` <img src="addons/deformablemesh/dm_icon_drag_deformer.svg" width="20"/> (In **Rest Pose Mode**, position the deformer, toggle it off, and deform the mesh by moving the node.)
<br>
Or **easily create your own** by extending the base class and overriding just a couple of methods in `dm_deformer.gd`.
## Getting Started
1. Download the [repository](https://github.com/cloudofoz/godot-deformablemesh/archive/refs/heads/main.zip) or download the *previous version* of the addon from the AssetLib in Godot ([link](https://godotengine.org/asset-library/asset/1794)).
2. Import the **addons** folder into your project.
3. Activate `DeformableMesh` under *Project > Project Settings > Plugins.*
<p align="center">
<img src="media/dm_getting_started_00.jpg" />
</p>
4. Add a *deformer* node to the scene.
<p align="center">
<img src="media/dm_getting_started_01.jpg" />
</p>
5. Add a `DeformableMeshInstance3D` node to the scene.
<p align="center">
<img src="media/dm_getting_started_02.jpg" />
</p>
6. Set the *mesh resource* you want to deform in the **Original Mesh** property.
<p align="center">
<img src="media/dm_getting_started_03.jpg" />
</p>
7. Link the *deformer node* you created before to the list of **Deformers** that will affect this mesh in the property panel.
<p align="center">
<img src="media/dm_getting_started_04.jpg" />
</p>
7. Tweak the *deformer* properties to achieve the desired result.
<p align="center">
<img src="media/dm_getting_started_05.jpg" />
</p>
## Example Project
1. [**You can download here**](media/dm_example_scene.zip) an example project that shows the basic functionalities of `DeformableMesh`.
2. Unzip the file
3. Import the project with Godot Engine 4+
4. Open the scene `dm_example_scene_v030.tscn` (if it's not already opened)
You can now try tweaking the deformer parameters.
Some effects are also controlled by the positions and the rotations of the deformer nodes.
`DeformableMesh` can apply multiple deformers like in a stack, so the order is important to achieve the correct effect.
You need also to specify the correct deformation axis (for some effects like bending, but it's not important with spherical deformers).
## Known Limitations
- This addon is designed with simplicity and versatility as primary goals, making it well-suited for simple, standard use cases. However, it is not optimized for specialized use cases, such as higher-density meshes (and, in some cases, multiple surfaces, which may also impact performance) in performance-critical applications. Users are encouraged to thoroughly test the addon to ensure it meets their specific requirements.
- While other deformers support deforming multiple meshes at once, a `DragDeformer` can only be tied to a single mesh at a time.
## Credits
I wish to thank the community for any contribution or feedback. Special thanks to:
- **Kevin Loustau**, for sparking the idea behind the `DragDeformer` feature.
## Changelog
**v0.40**
- **Add**: `DragDeformer` node.
- **Add**: `_on_end_update()` overridable method for the deformer base class.
- **Fix**: Removing a deformer now correctly unregisters the linked event listeners.
- **Fix**: `_on_begin_update()` is now called only once, even with multiple surfaces.
[**v0.30**](https://github.com/cloudofoz/godot-deformablemesh/releases/tag/v0.30)
- **Add**: `StandardDeformer` (Bend, Twist, and Taper).
- **Remove**: `BendDeformer` (now included as part of `StandardDeformer`).
- **Improve**: Deformer selection list.
[**v0.20**](https://github.com/cloudofoz/godot-deformablemesh/releases/tag/v0.20)
- **Add**: Bend deformers.
- **Add**: Base class to easily create custom deformers.
- **Refactor**: Codebase and minor improvements.
[**v0.10**](https://github.com/cloudofoz/godot-deformablemesh/tree/v0.1)
- **First release**
## License
[MIT License](/LICENSE.md)
================================================
FILE: addons/deformablemesh/dm_debug_sphere_material.tres
================================================
[gd_resource type="StandardMaterial3D" format=3 uid="uid://dwqr7ia23sngt"]
[resource]
depth_draw_mode = 2
no_depth_test = true
shading_mode = 0
diffuse_mode = 3
disable_ambient_light = true
albedo_color = Color(0.14902, 1, 0.713726, 1)
================================================
FILE: addons/deformablemesh/dm_debug_sphere_mesh.tres
================================================
[gd_resource type="ArrayMesh" load_steps=2 format=4 uid="uid://b3o4hmiksh4it"]
[ext_resource type="Material" uid="uid://dwqr7ia23sngt" path="res://addons/deformablemesh/dm_debug_sphere_material.tres" id="1_d8wbp"]
[resource]
_surfaces = [{
"aabb": AABB(-0.990686, 0, -0.997669, 1.9907, 1e-05, 1.99534),
"format": 34359738369,
"material": ExtResource("1_d8wbp"),
"primitive": 2,
"uv_scale": Vector4(0, 0, 0, 0),
"vertex_count": 24,
"vertex_data": PackedByteArray("AACAPwAAAAAAAAAAv4F2PwAAAADNIoo+O7taPwAAAAB0AwU/zrsuPwAAAAARGDs/pY3rPgAAAAByTGM/xlZQPgAAAABBpXo/rsKLvQAAAAA4Z38/V3WrvgAAAAADOHE/UqETvwAAAADwJFE/BJVGvwAAAAD7jiE/XM5qvwAAAAA6+8s+mJ19vwAAAABFbws+mJ19vwAAAABFbwu+XM5qvwAAAAA6+8u+BJVGvwAAAAD7jiG/UqETvwAAAADwJFG/V3WrvgAAAAADOHG/rsKLvQAAAAA4Z3+/xlZQPgAAAABBpXq/pY3rPgAAAAByTGO/zrsuPwAAAAARGDu/O7taPwAAAAB0AwW/v4F2PwAAAADNIoq+AACAPwAAAAAAMI2l")
}, {
"aabb": AABB(-4.37114e-08, -0.990686, -0.997669, 1e-05, 1.9907, 1.99534),
"format": 34359738369,
"material": ExtResource("1_d8wbp"),
"primitive": 2,
"uv_scale": Vector4(0, 0, 0, 0),
"vertex_count": 24,
"vertex_data": PackedByteArray("Lr07swAAgD8AAAAA8cY0s7+Bdj/NIoo+aGggszu7Wj90AwU/UCQAs867Lj8RGDs/ib6ssqWN6z5yTGM/WMkYssZWUD5BpXo/+vxMMa7Ci704Z38/1np7Mld1q74DOHE/34fYMlKhE7/wJFE/mqERMwSVRr/7jiE/QjIsM1zOar86+8s+if05M5idfb9Fbws+if05M5idfb9Fbwu+QjIsM1zOar86+8u+mqERMwSVRr/7jiG/34fYMlKhE7/wJFG/1np7Mld1q74DOHG/+vxMMa7Ci704Z3+/WMkYssZWUD5BpXq/ib6ssqWN6z5yTGO/UCQAs867Lj8RGDu/aGggszu7Wj90AwW/8cY0s7+Bdj/NIoq+Lr07swAAgD8AMI2l")
}, {
"aabb": AABB(-0.990686, -0.997669, -4.36095e-08, 1.9907, 1.99534, 1.00436e-05),
"format": 34359738369,
"material": ExtResource("1_d8wbp"),
"primitive": 2,
"uv_scale": Vector4(0, 0, 0, 0),
"vertex_count": 24,
"vertex_data": PackedByteArray("AACAPwAAAAAAAAAAv4F2P80iij4Bm0qyO7taP3QDBT+iF8OyzrsuPxEYOz/XNAmzpY3rPnJMYz/QsCazxlZQPkGlej/wzzezrsKLvThnfz8jTTuzV3WrvgM4cT8s5jCzUqETv/AkUT+JYBmzBJVGv/uOIT+q9eyyXM5qvzr7yz5Al5WymJ19v0VvCz6jgsyxmJ19v0VvC76jgswxXM5qvzr7y75Al5UyBJVGv/uOIb+q9ewyUqETv/AkUb+JYBkzV3WrvgM4cb8s5jAzrsKLvThnf78jTTszxlZQPkGler/wzzczpY3rPnJMY7/QsCYzzrsuPxEYO7/XNAkzO7taP3QDBb+iF8Myv4F2P80iir4Bm0oyAACAPwAwjaXMFE8Z")
}]
================================================
FILE: addons/deformablemesh/dm_deformable_mesh.gd
================================================
# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends MeshInstance3D
#---------------------------------------------------------------------------------------------------
# CONSTANTS
#---------------------------------------------------------------------------------------------------
const SurfaceData = preload("dm_surface_data.gd")
const Deformer = preload("dm_deformer.gd")
#---------------------------------------------------------------------------------------------------
# PUBLIC VARIABLES
#---------------------------------------------------------------------------------------------------
@export_category("Deformable Mesh")
## Original mesh resource to be deformed
@export var original_mesh: Mesh = null:
set(value):
if(original_mesh):
original_mesh.changed.disconnect(dm_init_surfaces)
original_mesh = value
if(!original_mesh): return
original_mesh.changed.connect(dm_init_surfaces)
dm_init_surfaces()
## Array of deformer node paths that affects this mesh.
@onready @export var deformers: Array[Deformer]:
set(value):
deformers = value
dm_find_deformers()
#---------------------------------------------------------------------------------------------------
# PRIVATE VARIABLES
#---------------------------------------------------------------------------------------------------
var dm_surfaces = [SurfaceData];
var dm_need_update: bool = false
var dm_deformers: Array[Deformer]
#---------------------------------------------------------------------------------------------------
# VIRTUAL METHODS
#---------------------------------------------------------------------------------------------------
func _init():
set_notify_transform(true)
dm_clean_deformers()
func _ready():
dm_find_deformers()
func _process(_delta):
if(dm_need_update):
dm_update()
func _notification(what):
match what:
NOTIFICATION_TRANSFORM_CHANGED:
dm_need_update = true
NOTIFICATION_ENTER_WORLD:
dm_find_deformers()
#---------------------------------------------------------------------------------------------------
# PRIVATE METHODS
#---------------------------------------------------------------------------------------------------
func dm_init_surfaces():
if(!original_mesh): return
mesh = ArrayMesh.new()
var surface_count = original_mesh.get_surface_count()
dm_surfaces.clear()
for i in range(surface_count):
var s = SurfaceData.new()
s.create_from_surface(original_mesh, i)
dm_surfaces.push_back(s)
dm_update()
func dm_update():
if(dm_surfaces.size() < 1): return
if(!mesh): return
for deformer in dm_deformers:
if(!deformer.visible): continue
deformer._on_begin_update(self)
mesh.clear_surfaces()
for sidx in range(dm_surfaces.size()):
var s = dm_surfaces[sidx]
if(!s): continue
s.update_surface(dm_deformers, self)
s.commit_to_surface(mesh)
mesh.surface_set_material(sidx, original_mesh.surface_get_material(sidx))
dm_need_update = false
for deformer in dm_deformers:
if(!deformer.visible): continue
deformer._on_end_update()
func dm_clean_deformers():
#dm_deformers.clear()
while not dm_deformers.is_empty():
var deformer = dm_deformers.back()
deformer.on_deformer_updated.disconnect(_on_deformer_updated)
deformer.on_deformer_removed.disconnect(_on_deformer_removed)
dm_deformers.pop_back()
func dm_add_deformer(deformer: Deformer) -> void:
dm_deformers.push_back(deformer)
if(!deformer.on_deformer_updated.is_connected(_on_deformer_updated)):
deformer.on_deformer_updated.connect(_on_deformer_updated)
if(!deformer.on_deformer_removed.is_connected(_on_deformer_removed)):
deformer.on_deformer_removed.connect(_on_deformer_removed)
func dm_rem_deformer(deformer: Deformer) -> void:
var didx = dm_deformers.find(deformer)
if(didx > -1): dm_deformers.remove_at(didx)
if(deformer.on_deformer_updated.is_connected(_on_deformer_updated)):
deformer.on_deformer_updated.disconnect(_on_deformer_updated)
if(deformer.on_deformer_removed.is_connected(_on_deformer_removed)):
deformer.on_deformer_removed.disconnect(_on_deformer_removed)
func dm_find_deformers():
if(!is_inside_tree()): return
dm_clean_deformers()
for d in deformers:
if(d && !dm_deformers.has(d)):
dm_add_deformer(d)
else:
var didx = deformers.find(d)
deformers[didx] = null
notify_property_list_changed()
dm_need_update = true
#---------------------------------------------------------------------------------------------------
# CALLBACKS
#---------------------------------------------------------------------------------------------------
func _on_deformer_updated(deformer: Deformer):
var i = dm_deformers.find(deformer)
if( i == -1 ): dm_add_deformer(deformer)
dm_need_update = true
func _on_deformer_removed(deformer: Deformer):
dm_rem_deformer(deformer)
dm_need_update = true
#---------------------------------------------------------------------------------------------------
# KNOWN BUGS / LIMITATIONS
#---------------------------------------------------------------------------------------------------
#BUG: Error message when deleting node referenced through NodePath property or metadata #75168
# https://github.com/godotengine/godot/issues/75168
#LIMITATION: A deformer is currently only selectable from the scene tree
#LIMITATION: A deformable mesh is currently limited by one UV set
================================================
FILE: addons/deformablemesh/dm_deformable_mesh.gd.uid
================================================
uid://iakvtj364g6q
================================================
FILE: addons/deformablemesh/dm_deformer.gd
================================================
# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends MeshInstance3D
## This base class is useful to create your own deformers for the deformable meshes.
##
## Basic usage: extend this class and override these two methods:
##
## ## This method is called once before updating vertices.
## ## It's useful if you want to capture some information from the mesh that is going to
## ## to be modified. (for ex. the deformer position local to that istance)
## func _on_begin_update(DeformableMeshInstance3D) -> void
##
## ## This method is called for every vertex of the mesh. It takes the current vertex
## ## position and returns the new vertex position
## func _on_update_vertex(Vector3) -> Vector3
class_name DM_Deformer
#---------------------------------------------------------------------------------------------------
# CONSTANTS
#---------------------------------------------------------------------------------------------------
const DeformableMeshInstance3D = preload("dm_deformable_mesh.gd")
const DebugSphereMesh = preload("dm_debug_sphere_mesh.tres")
#---------------------------------------------------------------------------------------------------
# SIGNALS
#---------------------------------------------------------------------------------------------------
signal on_deformer_updated(deformer)
signal on_deformer_removed(deformer)
#---------------------------------------------------------------------------------------------------
# PUBLIC VARIABLES
#---------------------------------------------------------------------------------------------------
@export_category("Deformer")
## A debug mesh to show in the editor with this node
@export var debug_mesh: Mesh = DebugSphereMesh
## Draws the deformer mesh (only visible in editor mode)
@export var show_debug_mesh: bool = true:
set(value):
show_debug_mesh = value
mesh = debug_mesh if value else null
#---------------------------------------------------------------------------------------------------
# CALLBACKS
#---------------------------------------------------------------------------------------------------
func _on_user_changed_mesh():
if(show_debug_mesh && mesh != debug_mesh):
mesh = debug_mesh
#---------------------------------------------------------------------------------------------------
# VIRTUAL METHODS
#---------------------------------------------------------------------------------------------------
## Override this method to set up initial parameters in the deformer.
## Called once before every deformable mesh update.
func _on_begin_update(deformable: DeformableMeshInstance3D) -> void:
pass
## This is the main method to override for every type of deformer.
## The default behaviour will leave the vertex unchanged.
## It's called for every vertex of the deformable mesh.
func _on_update_vertex(mesh_vertex: Vector3) -> Vector3:
return mesh_vertex
## Override this method to perform final operations in the deformer.
## Called once after every deformable mesh update is finished.
func _on_end_update() -> void:
pass
func _init():
set_notify_transform(true)
if(!self.visibility_changed.is_connected(dm_update_deformables)):
self.visibility_changed.connect(dm_update_deformables)
if(Engine.is_editor_hint()):
if(!property_list_changed.is_connected(_on_user_changed_mesh)):
property_list_changed.connect(_on_user_changed_mesh)
func _ready():
if(Engine.is_editor_hint()):
if(show_debug_mesh):
mesh = debug_mesh
# scale = Vector3(radius, radius, radius) #TODO: Fix
else: show_debug_mesh = false
dm_update_deformables()
func _notification(what):
match what:
NOTIFICATION_TRANSFORM_CHANGED:
dm_update_deformables()
func _exit_tree():
on_deformer_removed.emit(self)
#---------------------------------------------------------------------------------------------------
# PRIVATE METHODS
#---------------------------------------------------------------------------------------------------
func dm_update_deformables():
if(!is_inside_tree()): return
on_deformer_updated.emit(self)
================================================
FILE: addons/deformablemesh/dm_deformer.gd.uid
================================================
uid://bi1bdmhkk4kui
================================================
FILE: addons/deformablemesh/dm_drag_deformer.gd
================================================
# Copyright (C) 2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends "dm_deformer.gd"
#---------------------------------------------------------------------------------------------------
# PUBLIC VARIABLES
#---------------------------------------------------------------------------------------------------
@export_category("Drag Deformer")
## Radius of the sphere affecting the mesh deformation.
@export var radius: float = 1.5:
set(value):
radius = value
dm_update_deformables()
self.scale = Vector3(radius, radius, radius)
## Falloff of the deformation effect, decreasing with distance from the center.
@export_exp_easing("attenuation") var attenuation: float = 1.0:
set(value):
attenuation = value
dm_update_deformables()
## Strength of the deformation effect.
@export_range(0, 2.0, 0.1, "or_greater") var strength: float = 1:
set(value):
strength = value
dm_update_deformables()
## Toggle the Rest Pose Mode.
## Enables setting the deformer position at which there is no deformation.
## When disabled, deformation is calculated from the rest pose.
@export var rest_pose: bool = true:
set(value):
if value == rest_pose:
return
rest_pose = value
if not rest_pose:
dm_compute_rest_distances = true
dm_update_deformables()
# Move the deformer to its rest position
elif is_instance_valid(dm_active_deformable):
self.global_position = dm_active_deformable.to_global(dm_rest_pos)
get:
return rest_pose
#---------------------------------------------------------------------------------------------------
# PRIVATE VARIABLES
#---------------------------------------------------------------------------------------------------
var dm_delta_translation: Vector3
var dm_rest_pos: Vector3
var dm_rest_distances: PackedFloat32Array
var dm_active_deformable: DeformableMeshInstance3D
var dm_active_mesh: Mesh
var dm_compute_rest_distances: bool = false
var dm_vertex_index: int
#---------------------------------------------------------------------------------------------------
# PRIVATE METHODS
#---------------------------------------------------------------------------------------------------
func dm_needs_computation(d: DeformableMeshInstance3D) -> bool:
# Check if the active deformable is different from the current one.
if dm_active_deformable != d:
# If the current deformer is associated with a different mesh, remove it.
if dm_active_deformable != null and dm_active_deformable.deformers.find(self) != -1:
d.deformers.remove_at(d.deformers.find(self))
d.dm_find_deformers()
push_warning("A DragDeformer can support only one mesh at a time.")
return true
# Check if the active mesh has changed.
if dm_active_mesh != d.original_mesh:
return true
# Check if rest distances need initialization.
if dm_rest_distances.is_empty():
return true
# No recomputation is needed.
return false
#---------------------------------------------------------------------------------------------------
# VIRTUAL METHODS
#---------------------------------------------------------------------------------------------------
func _ready():
dm_rest_pos = self.get_meta("dm_rest_pos", Vector3(0,0,0))
super._ready()
func _on_end_update() -> void:
# If the rest distances were computed during this frame, force a refresh.
# This ensures the meshes are correctly deformed immediately after loading
# a scene, avoiding the need to move the deformer to trigger the update.
if dm_compute_rest_distances:
dm_compute_rest_distances = false
dm_update_deformables()
func _on_begin_update(d: DeformableMeshInstance3D) -> void:
# When rest pose is active, only the rest position is stored,
# and no deformation occurs.
if rest_pose:
dm_rest_pos = d.to_local(self.global_position)
self.set_meta("dm_rest_pos", dm_rest_pos)
return
# Clear previous data if recomputation of distances is needed.
if dm_compute_rest_distances or dm_needs_computation(d):
dm_active_deformable = d
dm_active_mesh = d.original_mesh
dm_rest_distances.clear()
dm_compute_rest_distances = true
return
# Otherwise, compute the deformation translation vector.
dm_vertex_index = 0
dm_delta_translation = d.to_local(self.global_position) - dm_rest_pos
func _on_update_vertex(v: Vector3) -> Vector3:
# If in rest pose mode, return the vertex unchanged.
if rest_pose:
return v
# Compute and store rest distances if needed, without deforming the vertex.
if dm_compute_rest_distances:
var d = dm_rest_pos.distance_to(v)
dm_rest_distances.push_back(d)
return v
# Retrieve the rest distance for the current vertex.
var rest_distance = dm_rest_distances[dm_vertex_index]
dm_vertex_index += 1
var delta_radius = radius - rest_distance
if delta_radius > 0.0:
# Apply deformation using an eased interpolation factor.
var t = strength * ease(delta_radius / radius, attenuation)
v += dm_delta_translation * t
return v
================================================
FILE: addons/deformablemesh/dm_drag_deformer.gd.uid
================================================
uid://btbnin5d1s6y0
================================================
FILE: addons/deformablemesh/dm_spherical_deformer.gd
================================================
# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends "dm_deformer.gd"
#---------------------------------------------------------------------------------------------------
# PUBLIC VARIABLES
#---------------------------------------------------------------------------------------------------
@export_category("Spherical Deformer")
## Radius of the deformation sphere.
@export var radius: float = 1.5:
set(value):
radius = value
dm_update_deformables()
self.scale = Vector3(radius, radius, radius)
## Strength of the deformation.
@export var strength: float = 1.5:
set(value):
strength = value
dm_update_deformables()
## Falloff of the deformation, based on distance from the center.
@export_exp_easing("attenuation") var attenuation: float = 1.0:
set(value):
attenuation = value
dm_update_deformables()
#---------------------------------------------------------------------------------------------------
# PRIVATE VARIABLES
#---------------------------------------------------------------------------------------------------
var dm_deformable_local_pos: Vector3
#---------------------------------------------------------------------------------------------------
# VIRTUAL METHODS
#---------------------------------------------------------------------------------------------------
func _on_begin_update(d: DeformableMeshInstance3D) -> void:
dm_deformable_local_pos = d.to_local(self.global_position)
func _on_update_vertex(v: Vector3) -> Vector3:
var d = dm_deformable_local_pos.distance_to(v)
var delta = radius - d
if(delta <= 0): return v
var k = strength * ease(delta / radius, attenuation)
var n = (dm_deformable_local_pos - v).normalized()
v -= n * k
return v
================================================
FILE: addons/deformablemesh/dm_spherical_deformer.gd.uid
================================================
uid://y1tsa0v2pepl
================================================
FILE: addons/deformablemesh/dm_std_deformer.gd
================================================
# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends "dm_deformer.gd"
#---------------------------------------------------------------------------------------------------
# CONSTANTS
#---------------------------------------------------------------------------------------------------
const dm_eps = 0.0001
const dm_ref_axis = [Vector3.MODEL_LEFT, Vector3.MODEL_TOP, Vector3.MODEL_FRONT]
#---------------------------------------------------------------------------------------------------
# PUBLIC VARIABLES
#---------------------------------------------------------------------------------------------------
@export_category("Standard Deformer")
## Deformer Type
@export_enum("Bend:0", "Twist:1", "Taper:2")
var type = 0:
set(value):
type = value
dm_update_deformables()
## Main deformation axis.
@export_enum("X:0", "Y:1", "Z:2")
var main_axis = 1:
set(value):
main_axis = value
if value == second_axis:
if is_node_ready():
push_warning("Main axis has to be different from the secondary axis")
dm_axis_internal_update = true
second_axis = ( value + 1 ) % 3
dm_axis_internal_update = false
dm_third_axis = dm_find_third_axis(main_axis, second_axis)
dm_update_deformables()
# Secondary axis.
@export_enum("X:0", "Y:1", "Z:2")
var second_axis = 0:
set(value):
if value != main_axis:
second_axis = value
else:
if is_node_ready():
push_warning("Secondary axis has to be different from the main axis")
second_axis = ( value + 1 ) % 3
if !dm_axis_internal_update:
dm_third_axis = dm_find_third_axis(main_axis, second_axis)
dm_update_deformables()
@export_subgroup("Blend - Twist", "bending_")
## Bending / Twisting angle
@export_range(-360, +360, 5, "degrees", "or_greater", "or_less")
var bending_angle: int = 35:
set(value):
bending_angle = value
dm_bending_angle = deg_to_rad(value) if abs(value) >= dm_eps else dm_eps
dm_update_deformables()
@export_subgroup("Taper", "taper_")
# Taper Factor
@export_range(-2.0, 2.0, 0.25, "or_greater", "or_less")
var taper_factor: float = 0:
set(value):
taper_factor = value
dm_update_deformables()
#---------------------------------------------------------------------------------------------------
# PRIVATE VARIABLES
#---------------------------------------------------------------------------------------------------
var dm_axis_internal_update: bool = false
var dm_third_axis: float = 2
var dm_bending_angle: float = deg_to_rad(bending_angle)
var dm_radius: float
var dm_local_pos: Vector3
var dm_length: float
#---------------------------------------------------------------------------------------------------
# STATIC METHODS
#---------------------------------------------------------------------------------------------------
static func dm_vsub(v1: Vector3, v2: Vector3, i: int) -> float:
return abs(v1[i] - v2[i])
static func dm_find_third_axis(a: int, b: int) -> int:
for c in range(3):
if c != a && c != b:
return c
return -1
#---------------------------------------------------------------------------------------------------
# VIRTUAL METHODS
#---------------------------------------------------------------------------------------------------
func _on_begin_update(d: DeformableMeshInstance3D) -> void:
var aabb = d.original_mesh.get_aabb()
dm_local_pos = d.to_local(self.global_position)
dm_length = aabb.size[main_axis]
dm_radius = dm_length / dm_bending_angle
func _on_update_vertex(v: Vector3) -> Vector3:
match(type):
0: return dm_bend_deform(v)
1: return dm_twist_deform(v)
2: return dm_taper_deform(v)
return v
#---------------------------------------------------------------------------------------------------
# PRIVATE METHODS
#---------------------------------------------------------------------------------------------------
func dm_bend_deform(v: Vector3) -> Vector3:
var p = v.rotated(dm_ref_axis[main_axis], self.rotation[main_axis])
var alpha = dm_bending_angle * ( p[main_axis] - dm_local_pos[main_axis] ) / dm_length
var r = dm_radius + p[second_axis]
var out: Vector3
out[main_axis] = r * sin(alpha) + dm_local_pos[main_axis]
out[second_axis] = r * cos(alpha) - dm_radius
out[dm_third_axis] = p[dm_third_axis]
return out
func dm_twist_deform(v: Vector3) -> Vector3:
var alpha = dm_bending_angle * ( v[main_axis] - dm_local_pos[main_axis] ) / dm_length
var p = v.rotated(dm_ref_axis[main_axis], alpha)
return p
func dm_taper_deform(v: Vector3) -> Vector3:
var h = dm_length - dm_local_pos[main_axis] if v[main_axis] >= dm_local_pos[main_axis] else dm_local_pos[main_axis]
var f = taper_factor * (v[main_axis]- dm_local_pos[main_axis]) / h
var sec_axis_pos = v[second_axis] + v[second_axis] * f
if sign(sec_axis_pos) != sign(v[second_axis]): sec_axis_pos = 0
var trd_axis_pos = v[dm_third_axis] + v[dm_third_axis] * f
if sign(trd_axis_pos) != sign(v[dm_third_axis]): trd_axis_pos = 0
v[second_axis] = sec_axis_pos
v[dm_third_axis] = trd_axis_pos
return v
================================================
FILE: addons/deformablemesh/dm_std_deformer.gd.uid
================================================
uid://c0qauxqxaunnv
================================================
FILE: addons/deformablemesh/dm_surface_data.gd
================================================
# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends Object
#---------------------------------------------------------------------------------------------------
# CONSTANTS
#---------------------------------------------------------------------------------------------------
const Deformer = preload("dm_deformer.gd")
const DeformableMeshInstance3D = preload("dm_deformable_mesh.gd")
#---------------------------------------------------------------------------------------------------
# PRIVATE VARIABLES
#---------------------------------------------------------------------------------------------------
var dm_vpos: PackedVector3Array
var dm_uvcoords: PackedVector2Array
var dm_indices: PackedInt32Array
var dm_st: SurfaceTool = null
var dm_has_indices: bool = true
var dm_has_uv: bool = true
#---------------------------------------------------------------------------------------------------
# PRIVATE METHODS
#---------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------
# PUBLIC METHODS
#---------------------------------------------------------------------------------------------------
## Uses specified surface of given mesh to pupulate data of SurfaceData
func create_from_surface(mesh: Mesh, surface_index: int):
assert(mesh && surface_index >= 0)
if(!self.dm_st): self.dm_st = SurfaceTool.new()
else: self.dm_mesh_data.clear()
var dm_arrays = mesh.surface_get_arrays(surface_index)
dm_vpos = dm_arrays[Mesh.ARRAY_VERTEX]
if dm_arrays[Mesh.ARRAY_INDEX]:
dm_indices = dm_arrays[Mesh.ARRAY_INDEX]
else: dm_has_indices = false
if dm_arrays[Mesh.ARRAY_TEX_UV]:
dm_uvcoords = dm_arrays[Mesh.ARRAY_TEX_UV]
else: dm_has_uv = false
## Generate a deformed mesh surface from an array of deformers
func update_surface(deformers: Array[Deformer], deformable: DeformableMeshInstance3D) -> void:
assert(dm_st)
var vcount = dm_vpos.size()
var icount = dm_indices.size()
dm_st.clear()
dm_st.begin(Mesh.PRIMITIVE_TRIANGLES)
for vidx in range(vcount):
var v = dm_vpos[vidx]
for deformer in deformers:
if(!deformer.visible): continue
v = deformer._on_update_vertex(v)
if dm_has_uv:
dm_st.set_uv(dm_uvcoords[vidx])
dm_st.add_vertex(v)
if dm_has_indices:
for idx in range(icount):
dm_st.add_index(dm_indices[idx])
## Adds a new surface to a specified mesh with edited data
func commit_to_surface(mesh: ArrayMesh):
assert(mesh && dm_st)
dm_st.generate_normals()
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, dm_st.commit_to_arrays())
================================================
FILE: addons/deformablemesh/dm_surface_data.gd.uid
================================================
uid://dlrcubdpvkf5o
================================================
FILE: addons/deformablemesh/plugin.cfg
================================================
[plugin]
name="DeformableMesh"
description="This addon enables real-time deformation of 3D meshes using customizable deformers. This version includes the following deformer nodes: SphericalDeformer, SimpleDeformer (bend, twist, taper) and DragDeformer."
author="cloudofoz"
version="0.40"
script="plugin.gd"
================================================
FILE: addons/deformablemesh/plugin.gd
================================================
# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)
#
# 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.
@tool
extends EditorPlugin
func _enter_tree() -> void:
add_custom_type("DragDeformer", "MeshInstance3D", preload("dm_drag_deformer.gd"), preload("dm_icon_drag_deformer.svg"))
add_custom_type("SphericalDeformer", "MeshInstance3D", preload("dm_spherical_deformer.gd"), preload("dm_icon_spherical_deformer.svg"))
add_custom_type("StandardDeformer", "MeshInstance3D", preload("dm_std_deformer.gd"), preload("dm_icon_std_deformer.svg"))
add_custom_type("DeformableMeshInstance3D", "MeshInstance3D", preload("dm_deformable_mesh.gd"), preload("dm_icon_deformable_mesh.svg"))
func _exit_tree() -> void:
remove_custom_type("DragDeformer")
remove_custom_type("SphericalDeformer")
remove_custom_type("StandardDeformer")
remove_custom_type("DeformableMeshInstance3D")
================================================
FILE: addons/deformablemesh/plugin.gd.uid
================================================
uid://coabw0coyhvo6
gitextract_20c1fllx/
├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
└── addons/
└── deformablemesh/
├── dm_debug_sphere_material.tres
├── dm_debug_sphere_mesh.tres
├── dm_deformable_mesh.gd
├── dm_deformable_mesh.gd.uid
├── dm_deformer.gd
├── dm_deformer.gd.uid
├── dm_drag_deformer.gd
├── dm_drag_deformer.gd.uid
├── dm_spherical_deformer.gd
├── dm_spherical_deformer.gd.uid
├── dm_std_deformer.gd
├── dm_std_deformer.gd.uid
├── dm_surface_data.gd
├── dm_surface_data.gd.uid
├── plugin.cfg
├── plugin.gd
└── plugin.gd.uid
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (44K chars).
[
{
"path": ".gitattributes",
"chars": 231,
"preview": "# Normalize EOL for all files that Git considers text files.\n* text=auto eol=lf\n\n# Only include the addons folder when d"
},
{
"path": ".gitignore",
"chars": 36,
"preview": "# Godot 4+ specific ignores\n.godot/\n"
},
{
"path": "LICENSE.md",
"chars": 1078,
"preview": "MIT License\n\nCopyright (C) 2023 Claudio Z. (cloudofoz)\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "README.md",
"chars": 4722,
"preview": "  2023-2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "addons/deformablemesh/dm_deformable_mesh.gd.uid",
"chars": 19,
"preview": "uid://iakvtj364g6q\n"
},
{
"path": "addons/deformablemesh/dm_deformer.gd",
"chars": 5151,
"preview": "# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "addons/deformablemesh/dm_deformer.gd.uid",
"chars": 20,
"preview": "uid://bi1bdmhkk4kui\n"
},
{
"path": "addons/deformablemesh/dm_drag_deformer.gd",
"chars": 5960,
"preview": "# Copyright (C) 2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "addons/deformablemesh/dm_drag_deformer.gd.uid",
"chars": 20,
"preview": "uid://btbnin5d1s6y0\n"
},
{
"path": "addons/deformablemesh/dm_spherical_deformer.gd",
"chars": 2773,
"preview": "# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "addons/deformablemesh/dm_spherical_deformer.gd.uid",
"chars": 19,
"preview": "uid://y1tsa0v2pepl\n"
},
{
"path": "addons/deformablemesh/dm_std_deformer.gd",
"chars": 6045,
"preview": "# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "addons/deformablemesh/dm_std_deformer.gd.uid",
"chars": 20,
"preview": "uid://c0qauxqxaunnv\n"
},
{
"path": "addons/deformablemesh/dm_surface_data.gd",
"chars": 3699,
"preview": "# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "addons/deformablemesh/dm_surface_data.gd.uid",
"chars": 20,
"preview": "uid://dlrcubdpvkf5o\n"
},
{
"path": "addons/deformablemesh/plugin.cfg",
"chars": 308,
"preview": "[plugin]\n\nname=\"DeformableMesh\"\ndescription=\"This addon enables real-time deformation of 3D meshes using customizable de"
},
{
"path": "addons/deformablemesh/plugin.gd",
"chars": 1878,
"preview": "# Copyright (C) 2023-2024 Claudio Z. (cloudofoz)\n# \n# Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "addons/deformablemesh/plugin.gd.uid",
"chars": 20,
"preview": "uid://coabw0coyhvo6\n"
}
]
About this extraction
This page contains the full source code of the cloudofoz/godot-deformablemesh GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (39.8 KB), approximately 10.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.