diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index f7764d5e32c5..7b86afcc4c68 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -65,11 +65,15 @@ - + - Creates a new surface. - Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface. - The [param arrays] argument is an array of arrays. See [enum Mesh.ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used. + Creates a new surface. [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface. + Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType]. + The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used. + The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code]. + The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used. + The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY]. + [b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles. diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml index b80857a7bf87..10479dfcfe00 100644 --- a/doc/classes/ImporterMesh.xml +++ b/doc/classes/ImporterMesh.xml @@ -27,9 +27,13 @@ - Creates a new surface, analogous to [method ArrayMesh.add_surface_from_arrays]. - Surfaces are created to be rendered using a [param primitive], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines, or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface. - The [param arrays] argument is an array of arrays. See [enum Mesh.ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used. + Creates a new surface. [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface. + Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType]. + The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used. + The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code]. + The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used. + The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY]. + [b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles. diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 94e80ffb2b99..1c1f48588fa8 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -227,10 +227,10 @@ Contains custom color channel 3. [PackedByteArray] if [code](format >> [constant ARRAY_FORMAT_CUSTOM3_SHIFT]) & [constant ARRAY_FORMAT_CUSTOM_MASK])[/code] is [constant ARRAY_CUSTOM_RGBA8_UNORM], [constant ARRAY_CUSTOM_RGBA8_UNORM], [constant ARRAY_CUSTOM_RG_HALF] or [constant ARRAY_CUSTOM_RGBA_HALF]. [PackedFloat32Array] otherwise. - [PackedFloat32Array] or [PackedInt32Array] of bone indices. Each element is a group of 4 numbers. + [PackedFloat32Array] or [PackedInt32Array] of bone indices. Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag. - [PackedFloat32Array] of bone weights. Each element in groups of 4 floats. + [PackedFloat32Array] or [PackedFloat64Array] of bone weights in the range [code]0.0[/code] to [code]1.0[/code] (inclusive). Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag. [PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the *i*'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices. @@ -341,6 +341,9 @@ Flag used to mark that the mesh contains up to 8 bone influences per vertex. This flag indicates that [constant ARRAY_BONES] and [constant ARRAY_WEIGHTS] elements will have double length. + + Flag used to mark that the mesh intentionally contains no vertex array. + Blend shapes are normalized. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 32281953b2bd..887fcb21978f 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3810,6 +3810,8 @@ + + Primitive to draw consists of points. diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 5e18b5df37a2..cedf4319f8ba 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -687,6 +687,7 @@ void Mesh::_bind_methods() { BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES); BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE); BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS); + BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); @@ -1555,6 +1556,7 @@ void ArrayMesh::_recompute_aabb() { // TODO: Need to add binding to add_surface using future MeshSurfaceData object. void ArrayMesh::add_surface(BitField p_format, PrimitiveType p_primitive, const Vector &p_array, const Vector &p_attribute_array, const Vector &p_skin_array, int p_vertex_count, const Vector &p_index_array, int p_index_count, const AABB &p_aabb, const Vector &p_blend_shape_data, const Vector &p_bone_aabbs, const Vector &p_lods) { + ERR_FAIL_COND(surfaces.size() == RS::MAX_MESH_SURFACES); _create_if_empty(); Surface s; @@ -1590,6 +1592,7 @@ void ArrayMesh::add_surface(BitField p_format, PrimitiveType p_prim } void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray &p_blend_shapes, const Dictionary &p_lods, BitField p_flags) { + ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size()); ERR_FAIL_COND(p_arrays.size() != ARRAY_MAX); RS::SurfaceData surface; @@ -2058,7 +2061,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ArrayMesh::set_blend_shape_mode); ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode); - ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces); ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region); ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index e24b2af976b6..c3bd3d277f2d 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -996,6 +996,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa if (index_array_len) { List keys; p_lods.get_key_list(&keys); + keys.sort(); // otherwise lod levels may get skipped for (const Variant &E : keys) { float distance = E; ERR_CONTINUE(distance <= 0.0); @@ -1826,6 +1827,7 @@ void RenderingServer::_bind_methods() { BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_2D_VERTICES); BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_DYNAMIC_UPDATE); BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS); + BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY); BIND_ENUM_CONSTANT(PRIMITIVE_POINTS); BIND_ENUM_CONSTANT(PRIMITIVE_LINES); diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h index 4d9feeb4fac4..b2a2ecc3bf72 100644 --- a/tests/scene/test_arraymesh.h +++ b/tests/scene/test_arraymesh.h @@ -114,6 +114,17 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { CHECK(mesh->get_blend_shape_count() == 0); } + SUBCASE("Can't add surface with incorrect number of blend shapes.") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + Ref cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + ERR_PRINT_OFF + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + ERR_PRINT_ON + CHECK(mesh->get_surface_count() == 0); + } + SUBCASE("Can't clear blend shapes after surface had been added.") { mesh->add_blend_shape(name_a); mesh->add_blend_shape(name_b); @@ -121,7 +132,15 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { Array cylinder_array{}; cylinder_array.resize(Mesh::ARRAY_MAX); cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + Array blend_shape{}; + blend_shape.resize(Mesh::ARRAY_MAX); + blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX]; + blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL]; + blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT]; + Array blend_shapes{}; + blend_shapes.push_back(blend_shape); + blend_shapes.push_back(blend_shape); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes); ERR_PRINT_OFF mesh->clear_blend_shapes();