Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
- This file was copied to source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc.
| Show All 32 Lines | |||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| namespace blender::nodes { | namespace blender::nodes { | ||||
| static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) | static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) | ||||
| { | { | ||||
| b.add_input<decl::Geometry>("Geometry"); | b.add_input<decl::Geometry>("Volume"); | ||||
JacquesLucke: Guess we can also change the names to "Volume" and "Mesh` now? | |||||
Done Inline ActionsYeah, good idea HooglyBoogly: Yeah, good idea | |||||
| b.add_input<decl::String>("Density"); | |||||
| b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); | b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); | ||||
| b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); | b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); | ||||
| b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); | b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); | ||||
| b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); | b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); | ||||
| b.add_output<decl::Geometry>("Geometry"); | b.add_output<decl::Geometry>("Mesh"); | ||||
| } | } | ||||
| static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) | static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) | ||||
| { | { | ||||
| uiLayoutSetPropSep(layout, true); | uiLayoutSetPropSep(layout, true); | ||||
| uiLayoutSetPropDecorate(layout, false); | uiLayoutSetPropDecorate(layout, false); | ||||
| uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); | uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); | ||||
| } | } | ||||
| static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) | static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) | ||||
| { | { | ||||
| NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( | NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( | ||||
| sizeof(NodeGeometryVolumeToMesh), __func__); | sizeof(NodeGeometryVolumeToMesh), __func__); | ||||
| data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; | data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; | ||||
| bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); | |||||
| bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; | |||||
| STRNCPY(grid_socket_value->value, "density"); | |||||
| node->storage = data; | node->storage = data; | ||||
| } | } | ||||
| static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) | static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) | ||||
| { | { | ||||
| NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; | NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; | ||||
| bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); | bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); | ||||
| bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); | bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); | ||||
| nodeSetSocketAvailability(voxel_amount_socket, | nodeSetSocketAvailability(voxel_amount_socket, | ||||
| data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); | data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); | ||||
| nodeSetSocketAvailability(voxel_size_socket, | nodeSetSocketAvailability(voxel_size_socket, | ||||
| data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); | data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); | ||||
| } | } | ||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| static void create_mesh_from_volume(GeometrySet &geometry_set_in, | static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms) | ||||
| GeometrySet &geometry_set_out, | |||||
| GeoNodeExecParams ¶ms) | |||||
| { | { | ||||
| if (!geometry_set_in.has<VolumeComponent>()) { | |||||
| return; | |||||
| } | |||||
| const NodeGeometryVolumeToMesh &storage = | const NodeGeometryVolumeToMesh &storage = | ||||
| *(const NodeGeometryVolumeToMesh *)params.node().storage; | *(const NodeGeometryVolumeToMesh *)params.node().storage; | ||||
| bke::VolumeToMeshResolution resolution; | bke::VolumeToMeshResolution resolution; | ||||
| resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; | resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; | ||||
| if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { | if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { | ||||
| resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); | resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f); | ||||
| if (resolution.settings.voxel_amount <= 0.0f) { | |||||
| return; | |||||
| } | |||||
| } | } | ||||
| else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { | else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { | ||||
| resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); | resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f); | ||||
| if (resolution.settings.voxel_size <= 0.0f) { | |||||
| return; | |||||
| } | } | ||||
| return resolution; | |||||
| } | |||||
| static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> grids, | |||||
| const float threshold, | |||||
| const float adaptivity, | |||||
| const bke::VolumeToMeshResolution &resolution) | |||||
| { | |||||
| Array<bke::OpenVDBMeshData> mesh_data(grids.size()); | |||||
| for (const int i : grids.index_range()) { | |||||
| mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity); | |||||
| } | |||||
| int vert_offset = 0; | |||||
| int poly_offset = 0; | |||||
| int loop_offset = 0; | |||||
| Array<int> vert_offsets(mesh_data.size()); | |||||
| Array<int> poly_offsets(mesh_data.size()); | |||||
| Array<int> loop_offsets(mesh_data.size()); | |||||
| for (const int i : grids.index_range()) { | |||||
| const bke::OpenVDBMeshData &data = mesh_data[i]; | |||||
| vert_offsets[i] = vert_offset; | |||||
| poly_offsets[i] = poly_offset; | |||||
| loop_offsets[i] = loop_offset; | |||||
| vert_offset += data.verts.size(); | |||||
| poly_offset += (data.tris.size() + data.quads.size()); | |||||
| loop_offset += (3 * data.tris.size() + 4 * data.quads.size()); | |||||
| } | } | ||||
| const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); | Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); | ||||
| const Volume *volume = component->get_for_read(); | BKE_id_material_eval_ensure_default_slot(&mesh->id); | ||||
| MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; | |||||
| MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; | |||||
| MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; | |||||
| for (const int i : grids.index_range()) { | |||||
| const bke::OpenVDBMeshData &data = mesh_data[i]; | |||||
| bke::fill_mesh_from_openvdb_data(data.verts, | |||||
| data.tris, | |||||
| data.quads, | |||||
| vert_offsets[i], | |||||
| poly_offsets[i], | |||||
| loop_offsets[i], | |||||
| verts, | |||||
| polys, | |||||
| loops); | |||||
| } | |||||
| BKE_mesh_calc_edges(mesh, false, false); | |||||
| BKE_mesh_normals_tag_dirty(mesh); | |||||
| return mesh; | |||||
| } | |||||
| static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams ¶ms) | |||||
| { | |||||
| const Volume *volume = geometry_set.get_volume_for_read(); | |||||
| if (volume == nullptr) { | if (volume == nullptr) { | ||||
| return; | return nullptr; | ||||
| } | } | ||||
| const bke::VolumeToMeshResolution resolution = get_resolution_param(params); | |||||
| const Main *bmain = DEG_get_bmain(params.depsgraph()); | const Main *bmain = DEG_get_bmain(params.depsgraph()); | ||||
| BKE_volume_load(volume, bmain); | BKE_volume_load(volume, bmain); | ||||
| const std::string grid_name = params.get_input<std::string>("Density"); | Vector<openvdb::GridBase::ConstPtr> grids; | ||||
| const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); | for (const int i : IndexRange(BKE_volume_num_grids(volume))) { | ||||
| if (volume_grid == nullptr) { | const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); | ||||
| return; | openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); | ||||
| grids.append(std::move(grid)); | |||||
| } | } | ||||
| float threshold = params.get_input<float>("Threshold"); | if (grids.is_empty()) { | ||||
| float adaptivity = params.get_input<float>("Adaptivity"); | return nullptr; | ||||
| const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); | |||||
| Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); | |||||
| if (mesh == nullptr) { | |||||
| return; | |||||
| } | } | ||||
| BKE_id_material_eval_ensure_default_slot(&mesh->id); | |||||
| MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); | return create_mesh_from_volume_grids(grids, | ||||
| dst_component.replace(mesh); | params.get_input<float>("Threshold"), | ||||
| params.get_input<float>("Adaptivity"), | |||||
| resolution); | |||||
| } | } | ||||
| #endif /* WITH_OPENVDB */ | #endif /* WITH_OPENVDB */ | ||||
| static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) | static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) | ||||
| { | { | ||||
| GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); | GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); | ||||
| GeometrySet geometry_set_out; | |||||
| #ifdef WITH_OPENVDB | #ifdef WITH_OPENVDB | ||||
| create_mesh_from_volume(geometry_set_in, geometry_set_out, params); | geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { | ||||
| Mesh *mesh = create_mesh_from_volume(geometry_set, params); | |||||
| geometry_set.replace_mesh(mesh); | |||||
| geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); | |||||
Done Inline ActionsUse the keep_only method. JacquesLucke: Use the `keep_only` method. | |||||
| }); | |||||
| #else | #else | ||||
| params.error_message_add(NodeWarningType::Error, | params.error_message_add(NodeWarningType::Error, | ||||
| TIP_("Disabled, Blender was compiled without OpenVDB")); | TIP_("Disabled, Blender was compiled without OpenVDB")); | ||||
| #endif | #endif | ||||
| params.set_output("Geometry", geometry_set_out); | params.set_output("Mesh", std::move(geometry_set)); | ||||
| } | } | ||||
| } // namespace blender::nodes | } // namespace blender::nodes | ||||
| void register_node_type_geo_volume_to_mesh() | void register_node_type_geo_volume_to_mesh() | ||||
| { | { | ||||
| static bNodeType ntype; | static bNodeType ntype; | ||||
| Show All 11 Lines | |||||
Guess we can also change the names to "Volume" and "Mesh` now?