浏览代码

Consider gridmap collisions in navigation bake

rafallus 3 年之前
父节点
当前提交
cc46abd73d

+ 1 - 1
modules/gridmap/doc_classes/GridMap.xml

@@ -67,7 +67,7 @@
 				Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
 			</description>
 		</method>
-		<method name="get_meshes">
+		<method name="get_meshes" qualifiers="const">
 			<return type="Array" />
 			<description>
 				Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.

+ 21 - 2
modules/gridmap/grid_map.cpp

@@ -35,6 +35,7 @@
 #include "scene/3d/light_3d.h"
 #include "scene/resources/mesh_library.h"
 #include "scene/resources/physics_material.h"
+#include "scene/resources/primitive_meshes.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/scene_string_names.h"
 #include "servers/navigation_server_3d.h"
@@ -197,6 +198,24 @@ bool GridMap::get_collision_mask_value(int p_layer_number) const {
 	return get_collision_mask() & (1 << (p_layer_number - 1));
 }
 
+Array GridMap::get_collision_shapes() const {
+	Array shapes;
+	for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+		Octant *g = E.value;
+		RID body = g->static_body;
+		Transform3D body_xform = PhysicsServer3D::get_singleton()->body_get_state(body, PhysicsServer3D::BODY_STATE_TRANSFORM);
+		int nshapes = PhysicsServer3D::get_singleton()->body_get_shape_count(body);
+		for (int i = 0; i < nshapes; i++) {
+			RID shape = PhysicsServer3D::get_singleton()->body_get_shape(body, i);
+			Transform3D xform = PhysicsServer3D::get_singleton()->body_get_shape_transform(body, i);
+			shapes.push_back(body_xform * xform);
+			shapes.push_back(shape);
+		}
+	}
+
+	return shapes;
+}
+
 void GridMap::set_bake_navigation(bool p_bake_navigation) {
 	bake_navigation = p_bake_navigation;
 	_recreate_octant_data();
@@ -930,7 +949,7 @@ Array GridMap::get_used_cells() const {
 	return a;
 }
 
-Array GridMap::get_meshes() {
+Array GridMap::get_meshes() const {
 	if (mesh_library.is_null()) {
 		return Array();
 	}
@@ -938,7 +957,7 @@ Array GridMap::get_meshes() {
 	Vector3 ofs = _get_offset();
 	Array meshes;
 
-	for (KeyValue<IndexKey, Cell> &E : cell_map) {
+	for (const KeyValue<IndexKey, Cell> &E : cell_map) {
 		int id = E.value.item;
 		if (!mesh_library->has_item(id)) {
 			continue;

+ 3 - 1
modules/gridmap/grid_map.h

@@ -229,6 +229,8 @@ public:
 	void set_physics_material(Ref<PhysicsMaterial> p_material);
 	Ref<PhysicsMaterial> get_physics_material() const;
 
+	Array get_collision_shapes() const;
+
 	void set_bake_navigation(bool p_bake_navigation);
 	bool is_baking_navigation();
 
@@ -265,7 +267,7 @@ public:
 
 	Array get_used_cells() const;
 
-	Array get_meshes();
+	Array get_meshes() const;
 
 	void clear_baked_meshes();
 	void make_baked_meshes(bool p_gen_lightmap_uv = false, float p_lightmap_uv_texel_size = 0.1);

+ 94 - 8
modules/navigation/navigation_mesh_generator.cpp

@@ -265,14 +265,100 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
 	}
 
 #ifdef MODULE_GRIDMAP_ENABLED
-	if (Object::cast_to<GridMap>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
-		GridMap *gridmap_instance = Object::cast_to<GridMap>(p_node);
-		Array meshes = gridmap_instance->get_meshes();
-		Transform3D xform = gridmap_instance->get_transform();
-		for (int i = 0; i < meshes.size(); i += 2) {
-			Ref<Mesh> mesh = meshes[i + 1];
-			if (mesh.is_valid()) {
-				_add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+	GridMap *gridmap = Object::cast_to<GridMap>(p_node);
+
+	if (gridmap) {
+		if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
+			Array meshes = gridmap->get_meshes();
+			Transform3D xform = gridmap->get_transform();
+			for (int i = 0; i < meshes.size(); i += 2) {
+				Ref<Mesh> mesh = meshes[i + 1];
+				if (mesh.is_valid()) {
+					_add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+				}
+			}
+		}
+
+		if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES && (gridmap->get_collision_layer() & p_collision_mask)) {
+			Array shapes = gridmap->get_collision_shapes();
+			for (int i = 0; i < shapes.size(); i += 2) {
+				RID shape = shapes[i + 1];
+				PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape);
+				Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape);
+				Ref<Mesh> mesh;
+
+				switch (type) {
+					case PhysicsServer3D::SHAPE_SPHERE: {
+						real_t radius = data;
+						Ref<SphereMesh> sphere_mesh;
+						sphere_mesh.instantiate();
+						sphere_mesh->set_radius(radius);
+						sphere_mesh->set_height(radius * 2.0);
+						mesh = sphere_mesh;
+					} break;
+					case PhysicsServer3D::SHAPE_BOX: {
+						Vector3 extents = data;
+						Ref<BoxMesh> box_mesh;
+						box_mesh.instantiate();
+						box_mesh->set_size(2.0 * extents);
+						mesh = box_mesh;
+					} break;
+					case PhysicsServer3D::SHAPE_CAPSULE: {
+						Dictionary dict = data;
+						real_t radius = dict["radius"];
+						real_t height = dict["height"];
+						Ref<CapsuleMesh> capsule_mesh;
+						capsule_mesh.instantiate();
+						capsule_mesh->set_radius(radius);
+						capsule_mesh->set_height(height);
+						mesh = capsule_mesh;
+					} break;
+					case PhysicsServer3D::SHAPE_CYLINDER: {
+						Dictionary dict = data;
+						real_t radius = dict["radius"];
+						real_t height = dict["height"];
+						Ref<CylinderMesh> cylinder_mesh;
+						cylinder_mesh.instantiate();
+						cylinder_mesh->set_height(height);
+						cylinder_mesh->set_bottom_radius(radius);
+						cylinder_mesh->set_top_radius(radius);
+						mesh = cylinder_mesh;
+					} break;
+					case PhysicsServer3D::SHAPE_CONVEX_POLYGON: {
+						PackedVector3Array vertices = data;
+						Geometry3D::MeshData md;
+
+						Error err = ConvexHullComputer::convex_hull(vertices, md);
+
+						if (err == OK) {
+							PackedVector3Array faces;
+
+							for (int j = 0; j < md.faces.size(); ++j) {
+								Geometry3D::MeshData::Face face = md.faces[j];
+
+								for (int k = 2; k < face.indices.size(); ++k) {
+									faces.push_back(md.vertices[face.indices[0]]);
+									faces.push_back(md.vertices[face.indices[k - 1]]);
+									faces.push_back(md.vertices[face.indices[k]]);
+								}
+							}
+
+							_add_faces(faces, shapes[i], p_vertices, p_indices);
+						}
+					} break;
+					case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: {
+						Dictionary dict = data;
+						PackedVector3Array faces = Variant(dict["faces"]);
+						_add_faces(faces, shapes[i], p_vertices, p_indices);
+					} break;
+					default: {
+						WARN_PRINT("Unsupported collision shape type.");
+					} break;
+				}
+
+				if (mesh.is_valid()) {
+					_add_mesh(mesh, shapes[i], p_vertices, p_indices);
+				}
 			}
 		}
 	}