Browse Source

Add support for collision layers and masks in CSG shapes

meditator 6 years ago
parent
commit
0059930644
3 changed files with 153 additions and 9 deletions
  1. 90 9
      modules/csg/csg_shape.cpp
  2. 14 0
      modules/csg/csg_shape.h
  3. 49 0
      modules/csg/doc_classes/CSGShape.xml

+ 90 - 9
modules/csg/csg_shape.cpp

@@ -48,18 +48,76 @@ void CSGShape::set_use_collision(bool p_enable) {
 		PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
 		PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
 		PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
+		set_collision_layer(collision_layer);
+		set_collision_mask(collision_mask);
 		_make_dirty(); //force update
 	} else {
 		PhysicsServer::get_singleton()->free(root_collision_instance);
 		root_collision_instance = RID();
 		root_collision_shape.unref();
 	}
+	_change_notify();
 }
 
 bool CSGShape::is_using_collision() const {
 	return use_collision;
 }
 
+void CSGShape::set_collision_layer(uint32_t p_layer) {
+	collision_layer = p_layer;
+	if (root_collision_instance.is_valid()) {
+		PhysicsServer::get_singleton()->body_set_collision_layer(root_collision_instance, p_layer);
+	}
+}
+
+uint32_t CSGShape::get_collision_layer() const {
+
+	return collision_layer;
+}
+
+void CSGShape::set_collision_mask(uint32_t p_mask) {
+
+	collision_mask = p_mask;
+	if (root_collision_instance.is_valid()) {
+		PhysicsServer::get_singleton()->body_set_collision_mask(root_collision_instance, p_mask);
+	}
+}
+
+uint32_t CSGShape::get_collision_mask() const {
+
+	return collision_mask;
+}
+
+void CSGShape::set_collision_mask_bit(int p_bit, bool p_value) {
+
+	uint32_t mask = get_collision_mask();
+	if (p_value)
+		mask |= 1 << p_bit;
+	else
+		mask &= ~(1 << p_bit);
+	set_collision_mask(mask);
+}
+
+bool CSGShape::get_collision_mask_bit(int p_bit) const {
+
+	return get_collision_mask() & (1 << p_bit);
+}
+
+void CSGShape::set_collision_layer_bit(int p_bit, bool p_value) {
+
+	uint32_t mask = get_collision_layer();
+	if (p_value)
+		mask |= 1 << p_bit;
+	else
+		mask &= ~(1 << p_bit);
+	set_collision_layer(mask);
+}
+
+bool CSGShape::get_collision_layer_bit(int p_bit) const {
+
+	return get_collision_layer() & (1 << p_bit);
+}
+
 bool CSGShape::is_root_shape() const {
 
 	return !parent;
@@ -459,6 +517,8 @@ void CSGShape::_notification(int p_what) {
 			PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
 			PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
 			PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
+			set_collision_layer(collision_layer);
+			set_collision_mask(collision_mask);
 		}
 
 		_make_dirty();
@@ -477,7 +537,7 @@ void CSGShape::_notification(int p_what) {
 			parent->_make_dirty();
 		parent = NULL;
 
-		if (use_collision && is_root_shape()) {
+		if (use_collision && is_root_shape() && root_collision_instance.is_valid()) {
 			PhysicsServer::get_singleton()->free(root_collision_instance);
 			root_collision_instance = RID();
 			root_collision_shape.unref();
@@ -506,9 +566,12 @@ bool CSGShape::is_calculating_tangents() const {
 }
 
 void CSGShape::_validate_property(PropertyInfo &property) const {
-	if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) {
+	bool is_collision_prefixed = property.name.begins_with("collision_");
+	if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
 		//hide collision if not root
 		property.usage = PROPERTY_USAGE_NOEDITOR;
+	} else if (is_collision_prefixed && !bool(get("use_collision"))) {
+		property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
 	}
 }
 
@@ -520,34 +583,52 @@ void CSGShape::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation);
 	ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation);
 
+	ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
+	ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+
 	ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision);
 	ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision);
 
-	ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
-	ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+	ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CSGShape::set_collision_layer);
+	ClassDB::bind_method(D_METHOD("get_collision_layer"), &CSGShape::get_collision_layer);
+
+	ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape::set_collision_mask);
+	ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape::get_collision_mask);
+
+	ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape::set_collision_mask_bit);
+	ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape::get_collision_mask_bit);
+
+	ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape::set_collision_layer_bit);
+	ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape::get_collision_layer_bit);
 
 	ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents);
 	ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents);
 
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
 
+	ADD_GROUP("Collision", "collision_");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
 	BIND_ENUM_CONSTANT(OPERATION_UNION);
 	BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
 	BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION);
 }
 
 CSGShape::CSGShape() {
+	operation = OPERATION_UNION;
+	parent = NULL;
 	brush = NULL;
-	set_notify_local_transform(true);
 	dirty = false;
-	parent = NULL;
-	use_collision = false;
-	operation = OPERATION_UNION;
 	snap = 0.001;
+	use_collision = false;
+	collision_layer = 1;
+	collision_mask = 1;
 	calculate_tangents = true;
+	set_notify_local_transform(true);
 }
 
 CSGShape::~CSGShape() {

+ 14 - 0
modules/csg/csg_shape.h

@@ -61,6 +61,8 @@ private:
 	float snap;
 
 	bool use_collision;
+	uint32_t collision_layer;
+	uint32_t collision_mask;
 	Ref<ConcavePolygonShape> root_collision_shape;
 	RID root_collision_instance;
 
@@ -126,6 +128,18 @@ public:
 	void set_use_collision(bool p_enable);
 	bool is_using_collision() const;
 
+	void set_collision_layer(uint32_t p_layer);
+	uint32_t get_collision_layer() const;
+
+	void set_collision_mask(uint32_t p_mask);
+	uint32_t get_collision_mask() const;
+
+	void set_collision_layer_bit(int p_bit, bool p_value);
+	bool get_collision_layer_bit(int p_bit) const;
+
+	void set_collision_mask_bit(int p_bit, bool p_value);
+	bool get_collision_mask_bit(int p_bit) const;
+
 	void set_snap(float p_snap);
 	float get_snap() const;
 

+ 49 - 0
modules/csg/doc_classes/CSGShape.xml

@@ -18,6 +18,46 @@
 				Returns true if this is a root shape and is thus the object that is rendered.
 			</description>
 		</method>
+		<method name="get_collision_layer_bit" qualifiers="const">
+			<return type="bool">
+			</return>
+			<argument index="0" name="bit" type="int">
+			</argument>
+			<description>
+				Returns an individual bit on the collision mask.
+			</description>
+		</method>
+		<method name="get_collision_mask_bit" qualifiers="const">
+			<return type="bool">
+			</return>
+			<argument index="0" name="bit" type="int">
+			</argument>
+			<description>
+				Returns an individual bit on the collision mask.
+			</description>
+		</method>
+		<method name="set_collision_layer_bit">
+			<return type="void">
+			</return>
+			<argument index="0" name="bit" type="int">
+			</argument>
+			<argument index="1" name="value" type="bool">
+			</argument>
+			<description>
+				Sets individual bits on the layer mask. Use this if you only need to change one layer's value.
+			</description>
+		</method>
+		<method name="set_collision_mask_bit">
+			<return type="void">
+			</return>
+			<argument index="0" name="bit" type="int">
+			</argument>
+			<argument index="1" name="value" type="bool">
+			</argument>
+			<description>
+				Sets individual bits on the collision mask. Use this if you only need to change one layer's value.
+			</description>
+		</method>
 	</methods>
 	<members>
 		<member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents">
@@ -31,6 +71,15 @@
 		<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision">
 			Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden.
 		</member>
+		<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer">
+			The physics layers this area is in.
+			Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property.
+			A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A.
+		</member>
+		<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask">
+			The physics layers this CSG shape scans for collisions.
+		</member>
+	</members>
 	</members>
 	<constants>
 		<constant name="OPERATION_UNION" value="0" enum="Operation">