Merge pull request #46397 from trollodel/collisionobject3d-debug-shapes-3.2
Allow CollisionObject to show collision shape meshes
This commit is contained in:
commit
e919a413fb
|
@ -6206,6 +6206,7 @@ void SpatialEditor::_register_all_gizmos() {
|
||||||
add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
|
add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
|
||||||
add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
|
add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
|
||||||
add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));
|
add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));
|
||||||
|
add_gizmo_plugin(Ref<CollisionObjectGizmoPlugin>(memnew(CollisionObjectGizmoPlugin)));
|
||||||
add_gizmo_plugin(Ref<CollisionShapeSpatialGizmoPlugin>(memnew(CollisionShapeSpatialGizmoPlugin)));
|
add_gizmo_plugin(Ref<CollisionShapeSpatialGizmoPlugin>(memnew(CollisionShapeSpatialGizmoPlugin)));
|
||||||
add_gizmo_plugin(Ref<CollisionPolygonSpatialGizmoPlugin>(memnew(CollisionPolygonSpatialGizmoPlugin)));
|
add_gizmo_plugin(Ref<CollisionPolygonSpatialGizmoPlugin>(memnew(CollisionPolygonSpatialGizmoPlugin)));
|
||||||
add_gizmo_plugin(Ref<NavigationMeshSpatialGizmoPlugin>(memnew(NavigationMeshSpatialGizmoPlugin)));
|
add_gizmo_plugin(Ref<NavigationMeshSpatialGizmoPlugin>(memnew(NavigationMeshSpatialGizmoPlugin)));
|
||||||
|
|
|
@ -3029,6 +3029,57 @@ void BakedIndirectLightGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
|
||||||
|
|
||||||
////
|
////
|
||||||
|
|
||||||
|
CollisionObjectGizmoPlugin::CollisionObjectGizmoPlugin() {
|
||||||
|
const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
|
||||||
|
create_material("shape_material", gizmo_color);
|
||||||
|
const float gizmo_value = gizmo_color.get_v();
|
||||||
|
const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
|
||||||
|
create_material("shape_material_disabled", gizmo_color_disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CollisionObjectGizmoPlugin::has_gizmo(Spatial *p_spatial) {
|
||||||
|
return Object::cast_to<CollisionObject>(p_spatial) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
String CollisionObjectGizmoPlugin::get_name() const {
|
||||||
|
return "CollisionObject";
|
||||||
|
}
|
||||||
|
|
||||||
|
int CollisionObjectGizmoPlugin::get_priority() const {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionObjectGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
|
||||||
|
CollisionObject *co = Object::cast_to<CollisionObject>(p_gizmo->get_spatial_node());
|
||||||
|
|
||||||
|
p_gizmo->clear();
|
||||||
|
|
||||||
|
List<uint32_t> owners;
|
||||||
|
co->get_shape_owners(&owners);
|
||||||
|
for (List<uint32_t>::Element *E = owners.front(); E; E = E->next()) {
|
||||||
|
uint32_t owner_id = E->get();
|
||||||
|
Transform xform = co->shape_owner_get_transform(owner_id);
|
||||||
|
Object *owner = co->shape_owner_get_owner(owner_id);
|
||||||
|
// Exclude CollisionShape and CollisionPolygon as they have their gizmo.
|
||||||
|
if (!Object::cast_to<CollisionShape>(owner) && !Object::cast_to<CollisionPolygon>(owner)) {
|
||||||
|
Ref<Material> material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo);
|
||||||
|
for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) {
|
||||||
|
Ref<Shape> s = co->shape_owner_get_shape(owner_id, shape_id);
|
||||||
|
if (s.is_null()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SurfaceTool st;
|
||||||
|
st.append_from(s->get_debug_mesh(), 0, xform);
|
||||||
|
|
||||||
|
p_gizmo->add_mesh(st.commit(), false, Ref<SkinReference>(), material);
|
||||||
|
p_gizmo->add_collision_segments(s->get_debug_mesh_lines());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
CollisionShapeSpatialGizmoPlugin::CollisionShapeSpatialGizmoPlugin() {
|
CollisionShapeSpatialGizmoPlugin::CollisionShapeSpatialGizmoPlugin() {
|
||||||
const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
|
const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
|
||||||
create_material("shape_material", gizmo_color);
|
create_material("shape_material", gizmo_color);
|
||||||
|
|
|
@ -321,6 +321,19 @@ public:
|
||||||
BakedIndirectLightGizmoPlugin();
|
BakedIndirectLightGizmoPlugin();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CollisionObjectGizmoPlugin : public EditorSpatialGizmoPlugin {
|
||||||
|
|
||||||
|
GDCLASS(CollisionObjectGizmoPlugin, EditorSpatialGizmoPlugin);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool has_gizmo(Spatial *p_spatial);
|
||||||
|
String get_name() const;
|
||||||
|
int get_priority() const;
|
||||||
|
void redraw(EditorSpatialGizmo *p_gizmo);
|
||||||
|
|
||||||
|
CollisionObjectGizmoPlugin();
|
||||||
|
};
|
||||||
|
|
||||||
class CollisionShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
|
class CollisionShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
|
||||||
|
|
||||||
GDCLASS(CollisionShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
|
GDCLASS(CollisionShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "collision_object.h"
|
#include "collision_object.h"
|
||||||
|
|
||||||
|
#include "mesh_instance.h"
|
||||||
#include "scene/scene_string_names.h"
|
#include "scene/scene_string_names.h"
|
||||||
#include "servers/physics_server.h"
|
#include "servers/physics_server.h"
|
||||||
|
|
||||||
|
@ -113,6 +114,43 @@ void CollisionObject::_update_pickable() {
|
||||||
PhysicsServer::get_singleton()->body_set_ray_pickable(rid, pickable);
|
PhysicsServer::get_singleton()->body_set_ray_pickable(rid, pickable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollisionObject::_update_debug_shapes() {
|
||||||
|
for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) {
|
||||||
|
if (shapes.has(shapedata_idx->get())) {
|
||||||
|
ShapeData &shapedata = shapes[shapedata_idx->get()];
|
||||||
|
for (int i = 0; i < shapedata.shapes.size(); i++) {
|
||||||
|
ShapeData::ShapeBase &s = shapedata.shapes.write[i];
|
||||||
|
if (s.debug_shape) {
|
||||||
|
s.debug_shape->queue_delete();
|
||||||
|
s.debug_shape = nullptr;
|
||||||
|
}
|
||||||
|
if (s.shape.is_null() || shapedata.disabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Mesh> mesh = s.shape->get_debug_mesh();
|
||||||
|
MeshInstance *mi = memnew(MeshInstance);
|
||||||
|
mi->set_transform(shapedata.xform);
|
||||||
|
mi->set_mesh(mesh);
|
||||||
|
add_child(mi);
|
||||||
|
|
||||||
|
mi->force_update_transform();
|
||||||
|
s.debug_shape = mi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_shapes_to_update.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionObject::_update_shape_data(uint32_t p_owner) {
|
||||||
|
if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
|
||||||
|
if (debug_shapes_to_update.empty()) {
|
||||||
|
call_deferred("_update_debug_shapes");
|
||||||
|
}
|
||||||
|
debug_shapes_to_update.insert(p_owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CollisionObject::set_ray_pickable(bool p_ray_pickable) {
|
void CollisionObject::set_ray_pickable(bool p_ray_pickable) {
|
||||||
|
|
||||||
ray_pickable = p_ray_pickable;
|
ray_pickable = p_ray_pickable;
|
||||||
|
@ -147,6 +185,8 @@ void CollisionObject::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject::shape_owner_clear_shapes);
|
ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject::shape_owner_clear_shapes);
|
||||||
ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject::shape_find_owner);
|
ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject::shape_find_owner);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("_update_debug_shapes"), &CollisionObject::_update_debug_shapes);
|
||||||
|
|
||||||
BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx")));
|
BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx")));
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx")));
|
ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx")));
|
||||||
|
@ -196,6 +236,7 @@ void CollisionObject::shape_owner_set_disabled(uint32_t p_owner, bool p_disabled
|
||||||
PhysicsServer::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
|
PhysicsServer::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_update_shape_data(p_owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollisionObject::is_shape_owner_disabled(uint32_t p_owner) const {
|
bool CollisionObject::is_shape_owner_disabled(uint32_t p_owner) const {
|
||||||
|
@ -235,6 +276,8 @@ void CollisionObject::shape_owner_set_transform(uint32_t p_owner, const Transfor
|
||||||
PhysicsServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
|
PhysicsServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_update_shape_data(p_owner);
|
||||||
}
|
}
|
||||||
Transform CollisionObject::shape_owner_get_transform(uint32_t p_owner) const {
|
Transform CollisionObject::shape_owner_get_transform(uint32_t p_owner) const {
|
||||||
|
|
||||||
|
@ -259,6 +302,7 @@ void CollisionObject::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape> &
|
||||||
ShapeData::ShapeBase s;
|
ShapeData::ShapeBase s;
|
||||||
s.index = total_subshapes;
|
s.index = total_subshapes;
|
||||||
s.shape = p_shape;
|
s.shape = p_shape;
|
||||||
|
|
||||||
if (area) {
|
if (area) {
|
||||||
PhysicsServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
|
PhysicsServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
|
||||||
} else {
|
} else {
|
||||||
|
@ -267,6 +311,8 @@ void CollisionObject::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape> &
|
||||||
sd.shapes.push_back(s);
|
sd.shapes.push_back(s);
|
||||||
|
|
||||||
total_subshapes++;
|
total_subshapes++;
|
||||||
|
|
||||||
|
_update_shape_data(p_owner);
|
||||||
}
|
}
|
||||||
int CollisionObject::shape_owner_get_shape_count(uint32_t p_owner) const {
|
int CollisionObject::shape_owner_get_shape_count(uint32_t p_owner) const {
|
||||||
|
|
||||||
|
@ -294,13 +340,19 @@ void CollisionObject::shape_owner_remove_shape(uint32_t p_owner, int p_shape) {
|
||||||
ERR_FAIL_COND(!shapes.has(p_owner));
|
ERR_FAIL_COND(!shapes.has(p_owner));
|
||||||
ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size());
|
ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size());
|
||||||
|
|
||||||
int index_to_remove = shapes[p_owner].shapes[p_shape].index;
|
const ShapeData::ShapeBase &s = shapes[p_owner].shapes[p_shape];
|
||||||
|
int index_to_remove = s.index;
|
||||||
|
|
||||||
if (area) {
|
if (area) {
|
||||||
PhysicsServer::get_singleton()->area_remove_shape(rid, index_to_remove);
|
PhysicsServer::get_singleton()->area_remove_shape(rid, index_to_remove);
|
||||||
} else {
|
} else {
|
||||||
PhysicsServer::get_singleton()->body_remove_shape(rid, index_to_remove);
|
PhysicsServer::get_singleton()->body_remove_shape(rid, index_to_remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s.debug_shape) {
|
||||||
|
s.debug_shape->queue_delete();
|
||||||
|
}
|
||||||
|
|
||||||
shapes[p_owner].shapes.remove(p_shape);
|
shapes[p_owner].shapes.remove(p_shape);
|
||||||
|
|
||||||
for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
|
for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
|
||||||
|
|
|
@ -47,6 +47,7 @@ class CollisionObject : public Spatial {
|
||||||
Object *owner;
|
Object *owner;
|
||||||
Transform xform;
|
Transform xform;
|
||||||
struct ShapeBase {
|
struct ShapeBase {
|
||||||
|
Node *debug_shape = nullptr;
|
||||||
Ref<Shape> shape;
|
Ref<Shape> shape;
|
||||||
int index;
|
int index;
|
||||||
};
|
};
|
||||||
|
@ -67,8 +68,12 @@ class CollisionObject : public Spatial {
|
||||||
bool capture_input_on_drag;
|
bool capture_input_on_drag;
|
||||||
bool ray_pickable;
|
bool ray_pickable;
|
||||||
|
|
||||||
|
Set<uint32_t> debug_shapes_to_update;
|
||||||
|
|
||||||
void _update_pickable();
|
void _update_pickable();
|
||||||
|
|
||||||
|
void _update_shape_data(uint32_t p_owner);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CollisionObject(RID p_rid, bool p_area);
|
CollisionObject(RID p_rid, bool p_area);
|
||||||
|
|
||||||
|
@ -79,6 +84,8 @@ protected:
|
||||||
virtual void _mouse_enter();
|
virtual void _mouse_enter();
|
||||||
virtual void _mouse_exit();
|
virtual void _mouse_exit();
|
||||||
|
|
||||||
|
void _update_debug_shapes();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint32_t create_shape_owner(Object *p_owner);
|
uint32_t create_shape_owner(Object *p_owner);
|
||||||
void remove_shape_owner(uint32_t owner);
|
void remove_shape_owner(uint32_t owner);
|
||||||
|
|
|
@ -89,9 +89,6 @@ void CollisionShape::_notification(int p_what) {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
_update_in_shape_owner();
|
_update_in_shape_owner();
|
||||||
}
|
}
|
||||||
if (get_tree()->is_debugging_collisions_hint()) {
|
|
||||||
_update_debug_shape();
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
|
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
|
@ -162,7 +159,6 @@ void CollisionShape::_bind_methods() {
|
||||||
ClassDB::set_method_flags("CollisionShape", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
|
ClassDB::set_method_flags("CollisionShape", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape::_shape_changed);
|
ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape::_shape_changed);
|
||||||
ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape::_update_debug_shape);
|
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
|
||||||
|
@ -217,7 +213,6 @@ CollisionShape::CollisionShape() {
|
||||||
|
|
||||||
//indicator = VisualServer::get_singleton()->mesh_create();
|
//indicator = VisualServer::get_singleton()->mesh_create();
|
||||||
disabled = false;
|
disabled = false;
|
||||||
debug_shape = NULL;
|
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
owner_id = 0;
|
owner_id = 0;
|
||||||
set_notify_local_transform(true);
|
set_notify_local_transform(true);
|
||||||
|
@ -229,33 +224,9 @@ CollisionShape::~CollisionShape() {
|
||||||
//VisualServer::get_singleton()->free(indicator);
|
//VisualServer::get_singleton()->free(indicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionShape::_update_debug_shape() {
|
|
||||||
debug_shape_dirty = false;
|
|
||||||
|
|
||||||
if (debug_shape) {
|
|
||||||
debug_shape->queue_delete();
|
|
||||||
debug_shape = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Shape> s = get_shape();
|
|
||||||
if (s.is_null())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Ref<Mesh> mesh = s->get_debug_mesh();
|
|
||||||
MeshInstance *mi = memnew(MeshInstance);
|
|
||||||
mi->set_mesh(mesh);
|
|
||||||
add_child(mi);
|
|
||||||
debug_shape = mi;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionShape::_shape_changed() {
|
void CollisionShape::_shape_changed() {
|
||||||
// If this is a heightfield shape our center may have changed
|
// If this is a heightfield shape our center may have changed
|
||||||
if (parent) {
|
if (parent) {
|
||||||
_update_in_shape_owner(true);
|
_update_in_shape_owner(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) {
|
|
||||||
debug_shape_dirty = true;
|
|
||||||
call_deferred("_update_debug_shape");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,14 +44,10 @@ class CollisionShape : public Spatial {
|
||||||
uint32_t owner_id;
|
uint32_t owner_id;
|
||||||
CollisionObject *parent;
|
CollisionObject *parent;
|
||||||
|
|
||||||
Node *debug_shape;
|
|
||||||
bool debug_shape_dirty;
|
|
||||||
|
|
||||||
void resource_changed(RES res);
|
void resource_changed(RES res);
|
||||||
bool disabled;
|
bool disabled;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _update_debug_shape();
|
|
||||||
void _shape_changed();
|
void _shape_changed();
|
||||||
|
|
||||||
void _update_in_shape_owner(bool p_xform_only = false);
|
void _update_in_shape_owner(bool p_xform_only = false);
|
||||||
|
|
Loading…
Reference in New Issue