Improve NavigationMesh typing, parameter validation and documentation

This commit is contained in:
Haoyu Qiu 2021-08-11 16:23:14 +08:00
parent a902f76063
commit 60298328ca
5 changed files with 126 additions and 50 deletions

View file

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NavigationMesh" inherits="Resource" version="4.0">
<brief_description>
A mesh to approximate the walkable areas and obstacles.
</brief_description>
<description>
A navigation mesh is a collection of polygons that define which areas of an environment are traversable to aid agents in pathfinding through complicated spaces.
</description>
<tutorials>
<link title="3D Navmesh Demo">https://godotengine.org/asset-library/asset/124</link>
@ -12,39 +14,46 @@
<return type="void" />
<argument index="0" name="polygon" type="PackedInt32Array" />
<description>
Adds a polygon using the indices of the vertices you get when calling [method get_vertices].
</description>
</method>
<method name="clear_polygons">
<return type="void" />
<description>
Clears the array of polygons, but it doesn't clear the array of vertices.
</description>
</method>
<method name="create_from_mesh">
<return type="void" />
<argument index="0" name="mesh" type="Mesh" />
<description>
Initializes the navigation mesh by setting the vertices and indices according to a [Mesh].
</description>
</method>
<method name="get_collision_mask_bit" qualifiers="const">
<return type="bool" />
<argument index="0" name="bit" type="int" />
<description>
Returns whether the specified [code]bit[/code] of the [member geometry/collision_mask] is set.
</description>
</method>
<method name="get_polygon">
<return type="PackedInt32Array" />
<argument index="0" name="idx" type="int" />
<description>
Returns a [PackedInt32Array] containing the indices of the vertices of a created polygon.
</description>
</method>
<method name="get_polygon_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of polygons in the navigation mesh.
</description>
</method>
<method name="get_vertices" qualifiers="const">
<return type="PackedVector3Array" />
<description>
Returns a [PackedVector3Array] containing all the vertices being used to create the polygons.
</description>
</method>
<method name="set_collision_mask_bit">
@ -52,84 +61,127 @@
<argument index="0" name="bit" type="int" />
<argument index="1" name="value" type="bool" />
<description>
If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the [member geometry/collision_mask].
If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the [member geometry/collision_mask].
</description>
</method>
<method name="set_vertices">
<return type="void" />
<argument index="0" name="vertices" type="PackedVector3Array" />
<description>
Sets the vertices that can be then indexed to create polygons with the [method add_polygon] method.
</description>
</method>
</methods>
<members>
<member name="agent/height" type="float" setter="set_agent_height" getter="get_agent_height" default="2.0">
The minimum Y space needed for navigation to be generated.
The minimum floor to ceiling height that will still allow the floor area to be considered walkable.
[b]Note:[/b] While baking, this value will be rounded up to the nearest multiple of [member cell/height].
</member>
<member name="agent/max_climb" type="float" setter="set_agent_max_climb" getter="get_agent_max_climb" default="0.9">
The maximum height difference between two areas for navigation to be generated between them.
The minimum ledge height that is considered to still be traversable.
[b]Note:[/b] While baking, this value will be rounded down to the nearest multiple of [member cell/height].
</member>
<member name="agent/max_slope" type="float" setter="set_agent_max_slope" getter="get_agent_max_slope" default="45.0">
The maximum angle a slope can be at for navigation to be generated on it.
The maximum slope that is considered walkable, in degrees.
</member>
<member name="agent/radius" type="float" setter="set_agent_radius" getter="get_agent_radius" default="0.6">
Determines where the edge of a navigation mesh is. This way an agent will not overlap with another mesh or stand over nothing.
The distance to erode/shrink the walkable area of the heightfield away from obstructions.
[b]Note:[/b] While baking, this value will be rounded up to the nearest multiple of [member cell/size].
</member>
<member name="cell/height" type="float" setter="set_cell_height" getter="get_cell_height" default="0.2">
The height of a cell.
The Y axis cell size to use for fields.
</member>
<member name="cell/size" type="float" setter="set_cell_size" getter="get_cell_size" default="0.3">
The size of cells in the [NavigationMesh].
The XZ plane cell size to use for fields.
</member>
<member name="detail/sample_distance" type="float" setter="set_detail_sample_distance" getter="get_detail_sample_distance" default="6.0">
The sampling distance to use when generating the detail mesh, in cell unit.
</member>
<member name="detail/sample_max_error" type="float" setter="set_detail_sample_max_error" getter="get_detail_sample_max_error" default="1.0">
The maximum distance the detail mesh surface should deviate from heightfield, in cell unit.
</member>
<member name="edge/max_error" type="float" setter="set_edge_max_error" getter="get_edge_max_error" default="1.3">
The maximum distance a simplfied contour's border edges should deviate the original raw contour.
</member>
<member name="edge/max_length" type="float" setter="set_edge_max_length" getter="get_edge_max_length" default="12.0">
The maximum allowed length for contour edges along the border of the mesh.
[b]Note:[/b] While baking, this value will be rounded up to the nearest multiple of [member cell/size].
</member>
<member name="filter/filter_walkable_low_height_spans" type="bool" setter="set_filter_walkable_low_height_spans" getter="get_filter_walkable_low_height_spans" default="false">
If [code]true[/code], marks walkable spans as not walkable if the clearance above the span is less than [member agent/height].
</member>
<member name="filter/ledge_spans" type="bool" setter="set_filter_ledge_spans" getter="get_filter_ledge_spans" default="false">
If [code]true[/code], marks spans that are ledges as non-walkable.
</member>
<member name="filter/low_hanging_obstacles" type="bool" setter="set_filter_low_hanging_obstacles" getter="get_filter_low_hanging_obstacles" default="false">
If [code]true[/code], marks non-walkable spans as walkable if their maximum is within [member agent/max_climb] of a walkable neighbor.
</member>
<member name="geometry/collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask">
The physics layers used to generate the [NavigationMesh].
The physics layers to scan for static colliders.
Only used when [member geometry/parsed_geometry_type] is [constant PARSED_GEOMETRY_STATIC_COLLIDERS] or [constant PARSED_GEOMETRY_BOTH].
</member>
<member name="geometry/parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" default="0">
What kind of geometry is used to generate the [NavigationMesh].
<member name="geometry/parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationMesh.ParsedGeometryType" default="0">
Determines which type of nodes will be parsed as geometry. See [enum ParsedGeometryType] for possible values.
</member>
<member name="geometry/source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" default="0">
Which geometry is used to generate the [NavigationMesh].
<member name="geometry/source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" enum="NavigationMesh.SourceGeometryMode" default="0">
The source of the geometry used when baking. See [enum SourceGeometryMode] for possible values.
</member>
<member name="geometry/source_group_name" type="StringName" setter="set_source_group_name" getter="get_source_group_name">
The name of the group that is used to generate the [NavigationMesh].
The name of the group to scan for geometry.
Only used when [member geometry/source_geometry_mode] is [constant SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN] or [constant SOURCE_GEOMETRY_GROUPS_EXPLICIT].
</member>
<member name="polygon/verts_per_poly" type="float" setter="set_verts_per_poly" getter="get_verts_per_poly" default="6.0">
The number of vertices to use per polygon. Higher values will improve performance at the cost of lower precision.
The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process.
</member>
<member name="region/merge_size" type="float" setter="set_region_merge_size" getter="get_region_merge_size" default="20.0">
If two adjacent regions' edges are separated by a distance lower than this value, the regions will be merged together.
Any regions with a size smaller than this will be merged with larger regions if possible.
[b]Note:[/b] This value will be squared to calculate the number of cells. For example, a value of 20 will set the number of cells to 400.
</member>
<member name="region/min_size" type="float" setter="set_region_min_size" getter="get_region_min_size" default="8.0">
The minimum size of a region for it to be created.
[b]Note:[/b] This value will be squared to calculate the minimum number of cells allowed to form isolated island areas. For example, a value of 8 will set the number of cells to 64.
</member>
<member name="sample_partition_type/sample_partition_type" type="int" setter="set_sample_partition_type" getter="get_sample_partition_type" default="0">
<member name="sample_partition_type/sample_partition_type" type="int" setter="set_sample_partition_type" getter="get_sample_partition_type" enum="NavigationMesh.SamplePartitionType" default="0">
Partitioning algorithm for creating the navigation mesh polys. See [enum SamplePartitionType] for possible values.
</member>
</members>
<constants>
<constant name="SAMPLE_PARTITION_WATERSHED" value="0">
<constant name="SAMPLE_PARTITION_WATERSHED" value="0" enum="SamplePartitionType">
Watershed partitioning. Generally the best choice if you precompute the navigation mesh, use this if you have large open areas.
</constant>
<constant name="SAMPLE_PARTITION_MONOTONE" value="1">
<constant name="SAMPLE_PARTITION_MONOTONE" value="1" enum="SamplePartitionType">
Monotone partitioning. Use this if you want fast navigation mesh generation.
</constant>
<constant name="SAMPLE_PARTITION_LAYERS" value="2">
<constant name="SAMPLE_PARTITION_LAYERS" value="2" enum="SamplePartitionType">
Layer partitioning. Good choice to use for tiled navigation mesh with medium and small sized tiles.
</constant>
<constant name="PARSED_GEOMETRY_MESH_INSTANCES" value="0">
<constant name="SAMPLE_PARTITION_MAX" value="3" enum="SamplePartitionType">
Represents the size of the [enum SamplePartitionType] enum.
</constant>
<constant name="PARSED_GEOMETRY_STATIC_COLLIDERS" value="1">
<constant name="PARSED_GEOMETRY_MESH_INSTANCES" value="0" enum="ParsedGeometryType">
Parses mesh instances as geometry. This includes [MeshInstance3D], [CSGShape3D], and [GridMap] nodes.
</constant>
<constant name="PARSED_GEOMETRY_BOTH" value="2">
<constant name="PARSED_GEOMETRY_STATIC_COLLIDERS" value="1" enum="ParsedGeometryType">
Parses [StaticBody3D] colliders as geometry. The collider should be in any of the layers specified by [member geometry/collision_mask].
</constant>
<constant name="PARSED_GEOMETRY_BOTH" value="2" enum="ParsedGeometryType">
Both [constant PARSED_GEOMETRY_MESH_INSTANCES] and [constant PARSED_GEOMETRY_STATIC_COLLIDERS].
</constant>
<constant name="PARSED_GEOMETRY_MAX" value="3" enum="ParsedGeometryType">
Represents the size of the [enum ParsedGeometryType] enum.
</constant>
<constant name="SOURCE_GEOMETRY_NAVMESH_CHILDREN" value="0" enum="SourceGeometryMode">
Scans the child nodes of [NavigationRegion3D] recursively for geometry.
</constant>
<constant name="SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN" value="1" enum="SourceGeometryMode">
Scans nodes in a group and their child nodes recursively for geometry. The group is specified by [member geometry/source_group_name].
</constant>
<constant name="SOURCE_GEOMETRY_GROUPS_EXPLICIT" value="2" enum="SourceGeometryMode">
Uses nodes in a group for geometry. The group is specified by [member geometry/source_group_name].
</constant>
<constant name="SOURCE_GEOMETRY_MAX" value="3" enum="SourceGeometryMode">
Represents the size of the [enum SourceGeometryMode] enum.
</constant>
</constants>
</class>

View file

@ -138,7 +138,7 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons
}
}
void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
Ref<Mesh> mesh = mesh_instance->get_mesh();
@ -515,7 +515,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse();
for (Node *E : parse_nodes) {
int geometry_type = p_nav_mesh->get_parsed_geometry_type();
NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type();
uint32_t collision_mask = p_nav_mesh->get_collision_mask();
bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
_parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children);

View file

@ -52,7 +52,7 @@ protected:
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies);
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
static void _build_recast_navigation_mesh(

View file

@ -64,22 +64,22 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
}
}
void NavigationMesh::set_sample_partition_type(int p_value) {
ERR_FAIL_COND(p_value >= SAMPLE_PARTITION_MAX);
partition_type = static_cast<SamplePartitionType>(p_value);
void NavigationMesh::set_sample_partition_type(SamplePartitionType p_value) {
ERR_FAIL_INDEX(p_value, SAMPLE_PARTITION_MAX);
partition_type = p_value;
}
int NavigationMesh::get_sample_partition_type() const {
return static_cast<int>(partition_type);
NavigationMesh::SamplePartitionType NavigationMesh::get_sample_partition_type() const {
return partition_type;
}
void NavigationMesh::set_parsed_geometry_type(int p_value) {
ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX);
parsed_geometry_type = static_cast<ParsedGeometryType>(p_value);
void NavigationMesh::set_parsed_geometry_type(ParsedGeometryType p_value) {
ERR_FAIL_INDEX(p_value, PARSED_GEOMETRY_MAX);
parsed_geometry_type = p_value;
notify_property_list_changed();
}
int NavigationMesh::get_parsed_geometry_type() const {
NavigationMesh::ParsedGeometryType NavigationMesh::get_parsed_geometry_type() const {
return parsed_geometry_type;
}
@ -107,13 +107,13 @@ bool NavigationMesh::get_collision_mask_bit(int p_bit) const {
return get_collision_mask() & (1 << p_bit);
}
void NavigationMesh::set_source_geometry_mode(int p_geometry_mode) {
void NavigationMesh::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) {
ERR_FAIL_INDEX(p_geometry_mode, SOURCE_GEOMETRY_MAX);
source_geometry_mode = static_cast<SourceGeometryMode>(p_geometry_mode);
source_geometry_mode = p_geometry_mode;
notify_property_list_changed();
}
int NavigationMesh::get_source_geometry_mode() const {
NavigationMesh::SourceGeometryMode NavigationMesh::get_source_geometry_mode() const {
return source_geometry_mode;
}
@ -126,6 +126,7 @@ StringName NavigationMesh::get_source_group_name() const {
}
void NavigationMesh::set_cell_size(float p_value) {
ERR_FAIL_COND(p_value <= 0);
cell_size = p_value;
}
@ -134,6 +135,7 @@ float NavigationMesh::get_cell_size() const {
}
void NavigationMesh::set_cell_height(float p_value) {
ERR_FAIL_COND(p_value <= 0);
cell_height = p_value;
}
@ -142,6 +144,7 @@ float NavigationMesh::get_cell_height() const {
}
void NavigationMesh::set_agent_height(float p_value) {
ERR_FAIL_COND(p_value < 0);
agent_height = p_value;
}
@ -150,6 +153,7 @@ float NavigationMesh::get_agent_height() const {
}
void NavigationMesh::set_agent_radius(float p_value) {
ERR_FAIL_COND(p_value < 0);
agent_radius = p_value;
}
@ -158,6 +162,7 @@ float NavigationMesh::get_agent_radius() {
}
void NavigationMesh::set_agent_max_climb(float p_value) {
ERR_FAIL_COND(p_value < 0);
agent_max_climb = p_value;
}
@ -166,6 +171,7 @@ float NavigationMesh::get_agent_max_climb() const {
}
void NavigationMesh::set_agent_max_slope(float p_value) {
ERR_FAIL_COND(p_value < 0 || p_value > 90);
agent_max_slope = p_value;
}
@ -174,6 +180,7 @@ float NavigationMesh::get_agent_max_slope() const {
}
void NavigationMesh::set_region_min_size(float p_value) {
ERR_FAIL_COND(p_value < 0);
region_min_size = p_value;
}
@ -182,6 +189,7 @@ float NavigationMesh::get_region_min_size() const {
}
void NavigationMesh::set_region_merge_size(float p_value) {
ERR_FAIL_COND(p_value < 0);
region_merge_size = p_value;
}
@ -190,6 +198,7 @@ float NavigationMesh::get_region_merge_size() const {
}
void NavigationMesh::set_edge_max_length(float p_value) {
ERR_FAIL_COND(p_value < 0);
edge_max_length = p_value;
}
@ -198,6 +207,7 @@ float NavigationMesh::get_edge_max_length() const {
}
void NavigationMesh::set_edge_max_error(float p_value) {
ERR_FAIL_COND(p_value < 0);
edge_max_error = p_value;
}
@ -206,6 +216,7 @@ float NavigationMesh::get_edge_max_error() const {
}
void NavigationMesh::set_verts_per_poly(float p_value) {
ERR_FAIL_COND(p_value < 3);
verts_per_poly = p_value;
}
@ -214,6 +225,7 @@ float NavigationMesh::get_verts_per_poly() const {
}
void NavigationMesh::set_detail_sample_distance(float p_value) {
ERR_FAIL_COND(p_value < 0);
detail_sample_distance = p_value;
}
@ -222,6 +234,7 @@ float NavigationMesh::get_detail_sample_distance() const {
}
void NavigationMesh::set_detail_sample_max_error(float p_value) {
ERR_FAIL_COND(p_value < 0);
detail_sample_max_error = p_value;
}
@ -460,14 +473,6 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons);
BIND_CONSTANT(SAMPLE_PARTITION_WATERSHED);
BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE);
BIND_CONSTANT(SAMPLE_PARTITION_LAYERS);
BIND_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
BIND_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
BIND_CONSTANT(PARSED_GEOMETRY_BOTH);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
@ -494,6 +499,21 @@ void NavigationMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/ledge_spans"), "set_filter_ledge_spans", "get_filter_ledge_spans");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans");
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_WATERSHED);
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MONOTONE);
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_LAYERS);
BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MAX);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_NAVMESH_CHILDREN);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT);
BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX);
}
void NavigationMesh::_validate_property(PropertyInfo &property) const {

View file

@ -109,11 +109,11 @@ protected:
public:
// Recast settings
void set_sample_partition_type(int p_value);
int get_sample_partition_type() const;
void set_sample_partition_type(SamplePartitionType p_value);
SamplePartitionType get_sample_partition_type() const;
void set_parsed_geometry_type(int p_value);
int get_parsed_geometry_type() const;
void set_parsed_geometry_type(ParsedGeometryType p_value);
ParsedGeometryType get_parsed_geometry_type() const;
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
@ -121,8 +121,8 @@ public:
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
void set_source_geometry_mode(int p_geometry_mode);
int get_source_geometry_mode() const;
void set_source_geometry_mode(SourceGeometryMode p_geometry_mode);
SourceGeometryMode get_source_geometry_mode() const;
void set_source_group_name(StringName p_group_name);
StringName get_source_group_name() const;
@ -190,4 +190,8 @@ public:
NavigationMesh();
};
VARIANT_ENUM_CAST(NavigationMesh::SamplePartitionType);
VARIANT_ENUM_CAST(NavigationMesh::ParsedGeometryType);
VARIANT_ENUM_CAST(NavigationMesh::SourceGeometryMode);
#endif // NAVIGATION_MESH_H