Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bind mesh merging functionality in MeshInstance #57661

Merged
merged 1 commit into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions doc/classes/MeshInstance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@
Returns the number of surface materials.
</description>
</method>
<method name="is_mergeable_with" qualifiers="const">
<return type="bool" />
<argument index="0" name="other_mesh_instance" type="Node" />
<description>
Returns [code]true[/code] if this [MeshInstance] can be merged with the specified [code]other_mesh_instance[/code], using the [method MeshInstance.merge_meshes] function.
In order to be mergeable, properties of the [MeshInstance] must match, and each surface must match, in terms of material, attributes and vertex format.
</description>
</method>
<method name="merge_meshes">
<return type="bool" />
<argument index="0" name="mesh_instances" type="Array" default="[ ]" />
<argument index="1" name="use_global_space" type="bool" default="false" />
<argument index="2" name="check_compatibility" type="bool" default="true" />
<description>
This function can merge together the data from several source [MeshInstance]s into a single destination [MeshInstance] (the MeshInstance the function is called from). This is primarily useful for improving performance by reducing the number of drawcalls and [Node]s.
Merging should only be attempted for simple meshes that do not contain animation.
The final vertices can either be returned in global space, or in local space relative to the destination [MeshInstance] global transform (the destination Node must be inside the [SceneTree] for local space to work).
The function will make a final check for compatibility between the [MeshInstance]s by default, this should always be used unless you have previously checked for compatibility using [method MeshInstance.is_mergeable_with]. If the compatibility check is omitted and the meshes are merged, you may see rendering errors.
[b]Note:[/b] The requirements for similarity between meshes are quite stringent. They can be checked using the [method MeshInstance.is_mergeable_with] function prior to calling [method MeshInstance.merge_meshes].
Also note that any initial data in the destination [MeshInstance] data will be discarded.
</description>
</method>
<method name="set_surface_material">
<return type="void" />
<argument index="0" name="surface" type="int" />
Expand Down
27 changes: 26 additions & 1 deletion scene/3d/mesh_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,27 @@ void MeshInstance::create_debug_tangents() {
}
}

bool MeshInstance::merge_meshes(Vector<Variant> p_list, bool p_use_global_space, bool p_check_compatibility) {
// bound function only support variants, so we need to convert to a list of MeshInstances
Vector<MeshInstance *> mis;

for (int n = 0; n < p_list.size(); n++) {
MeshInstance *mi = Object::cast_to<MeshInstance>(p_list[n]);
if (mi) {
if (mi != this) {
mis.push_back(mi);
} else {
ERR_PRINT("Destination MeshInstance cannot be a source.");
}
} else {
ERR_PRINT("Only MeshInstances can be merged.");
}
}

ERR_FAIL_COND_V(!mis.size(), "Array contains no MeshInstances");
return _merge_meshes(mis, p_use_global_space, p_check_compatibility);
}

bool MeshInstance::is_mergeable_with(Node *p_other) const {
const MeshInstance *mi = Object::cast_to<MeshInstance>(p_other);

Expand Down Expand Up @@ -1153,7 +1174,7 @@ bool MeshInstance::_triangle_is_degenerate(const Vector3 &p_a, const Vector3 &p_

// If p_check_compatibility is set to false you MUST have performed a prior check using
// is_mergeable_with, otherwise you could get mismatching surface formats leading to graphical errors etc.
bool MeshInstance::merge_meshes(Vector<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility) {
bool MeshInstance::_merge_meshes(Vector<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility) {
if (p_list.size() < 1) {
// should not happen but just in case
return false;
Expand Down Expand Up @@ -1302,6 +1323,10 @@ void MeshInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance::create_debug_tangents);
ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);

ClassDB::bind_method(D_METHOD("is_mergeable_with", "other_mesh_instance"), &MeshInstance::is_mergeable_with);
ClassDB::bind_method(D_METHOD("merge_meshes", "mesh_instances", "use_global_space", "check_compatibility"), &MeshInstance::merge_meshes, DEFVAL(Vector<Variant>()), DEFVAL(false), DEFVAL(true));
ClassDB::set_method_flags("MeshInstance", "merge_meshes", METHOD_FLAGS_DEFAULT);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
Expand Down
3 changes: 2 additions & 1 deletion scene/3d/mesh_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class MeshInstance : public GeometryInstance {

private:
// merging
bool _merge_meshes(Vector<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility);
bool _is_mergeable_with(const MeshInstance &p_other) const;
void _merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, PoolVector<Vector3> &r_verts, PoolVector<Vector3> &r_norms, PoolVector<real_t> &r_tangents, PoolVector<Color> &r_colors, PoolVector<Vector2> &r_uvs, PoolVector<Vector2> &r_uv2s, PoolVector<int> &r_inds);
bool _ensure_indices_valid(PoolVector<int> &r_indices, const PoolVector<Vector3> &p_verts) const;
Expand Down Expand Up @@ -146,7 +147,7 @@ class MeshInstance : public GeometryInstance {

// merging
bool is_mergeable_with(Node *p_other) const;
bool merge_meshes(Vector<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility);
bool merge_meshes(Vector<Variant> p_list, bool p_use_global_space, bool p_check_compatibility);

virtual AABB get_aabb() const;
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
Expand Down
9 changes: 8 additions & 1 deletion scene/3d/room_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2156,7 +2156,14 @@ void RoomManager::_merge_meshes_in_room(Room *p_room) {

_merge_log("\t\t" + merged->get_name());

if (merged->merge_meshes(merge_list, true, false)) {
// merge function takes a vector of variants
Vector<Variant> variant_merge_list;
variant_merge_list.resize(merge_list.size());
for (int i = 0; i < merge_list.size(); i++) {
variant_merge_list.set(i, merge_list[i]);
}

if (merged->merge_meshes(variant_merge_list, true, false)) {
// set all the source meshes to portal mode ignore so not shown
for (int i = 0; i < merge_list.size(); i++) {
merge_list[i]->set_portal_mode(CullInstance::PORTAL_MODE_IGNORE);
Expand Down