Geometry Nodes: Handle multiple grids in the volume to mesh node
In future use cases, a volume can contain many grids that represent the density information. In this case, it's better if the volume to mesh node creates a mesh based on all of the grids in the volume. This is also a benefit to share-ability, since one doesn't have to specify the grid name in the node. Instead, in the future we can have a way to split particular grids into separate volumes, if only one grid should be considered. The code changes are relatively simple: - Move the old volume to mesh node to the legacy folder. - Run the volume to mesh node on all instance geometry, like elsewhere. - Make the blenkernel's volume to mesh API a bit more specific. Differential Revision: https://developer.blender.org/D12997
This commit is contained in:
parent
7979dff9dc
commit
8ddfdfd2b2
Notes:
blender-bot
2023-09-08 04:55:43 +02:00
Referenced by issue #92168, Update volume to mesh node to remove grid name Referenced by issue #91668, How to deal with multiple volume grids in a geometry
|
@ -760,6 +760,7 @@ geometry_node_categories = [
|
|||
]),
|
||||
GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
|
||||
NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll),
|
||||
NodeItem("GeometryNodeLegacyVolumeToMesh", poll=geometry_nodes_legacy_poll),
|
||||
|
||||
NodeItem("GeometryNodeVolumeToMesh"),
|
||||
]),
|
||||
|
|
|
@ -1442,7 +1442,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||
#define GEO_NODE_COLLECTION_INFO 1023
|
||||
#define GEO_NODE_IS_VIEWPORT 1024
|
||||
#define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025
|
||||
#define GEO_NODE_VOLUME_TO_MESH 1026
|
||||
#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026
|
||||
#define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027
|
||||
#define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028
|
||||
#define GEO_NODE_SUBDIVIDE_MESH 1029
|
||||
|
@ -1548,6 +1548,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||
#define GEO_NODE_CURVE_TO_POINTS 1130
|
||||
#define GEO_NODE_INSTANCES_TO_POINTS 1131
|
||||
#define GEO_NODE_IMAGE_TEXTURE 1132
|
||||
#define GEO_NODE_VOLUME_TO_MESH 1133
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -160,6 +160,7 @@ bool BKE_volume_save(const struct Volume *volume,
|
|||
#ifdef __cplusplus
|
||||
# include "BLI_float3.hh"
|
||||
# include "BLI_float4x4.hh"
|
||||
# include "BLI_string_ref.hh"
|
||||
|
||||
bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max);
|
||||
|
||||
|
@ -167,6 +168,10 @@ bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::f
|
|||
# include <openvdb/openvdb.h>
|
||||
# include <openvdb/points/PointDataGrid.h>
|
||||
|
||||
VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume,
|
||||
blender::StringRef name,
|
||||
openvdb::GridBase::Ptr vdb_grid);
|
||||
|
||||
bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid,
|
||||
blender::float3 &r_min,
|
||||
blender::float3 &r_max);
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
@ -33,10 +35,40 @@ struct VolumeToMeshResolution {
|
|||
};
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
/**
|
||||
* The result of converting a volume grid to mesh data, in the format used by the OpenVDB API.
|
||||
*/
|
||||
struct OpenVDBMeshData {
|
||||
std::vector<openvdb::Vec3s> verts;
|
||||
std::vector<openvdb::Vec3I> tris;
|
||||
std::vector<openvdb::Vec4I> quads;
|
||||
bool is_empty() const
|
||||
{
|
||||
return verts.empty();
|
||||
}
|
||||
};
|
||||
|
||||
struct Mesh *volume_to_mesh(const openvdb::GridBase &grid,
|
||||
const VolumeToMeshResolution &resolution,
|
||||
const float threshold,
|
||||
const float adaptivity);
|
||||
|
||||
struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid,
|
||||
const VolumeToMeshResolution &resolution,
|
||||
const float threshold,
|
||||
const float adaptivity);
|
||||
|
||||
void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
|
||||
const Span<openvdb::Vec3I> vdb_tris,
|
||||
const Span<openvdb::Vec4I> vdb_quads,
|
||||
const int vert_offset,
|
||||
const int poly_offset,
|
||||
const int loop_offset,
|
||||
MutableSpan<MVert> verts,
|
||||
MutableSpan<MPoly> polys,
|
||||
MutableSpan<MLoop> loops);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -5753,6 +5753,7 @@ static void registerGeometryNodes()
|
|||
register_node_type_geo_legacy_select_by_handle_type();
|
||||
register_node_type_geo_legacy_select_by_material();
|
||||
register_node_type_geo_legacy_subdivision_surface();
|
||||
register_node_type_geo_legacy_volume_to_mesh();
|
||||
|
||||
register_node_type_geo_align_rotation_to_vector();
|
||||
register_node_type_geo_attribute_capture();
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "BLI_math.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -71,6 +72,7 @@ static CLG_LogRef LOG = {"bke.volume"};
|
|||
using blender::float3;
|
||||
using blender::float4x4;
|
||||
using blender::IndexRange;
|
||||
using blender::StringRef;
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <atomic>
|
||||
|
@ -1451,6 +1453,21 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume,
|
||||
const StringRef name,
|
||||
openvdb::GridBase::Ptr vdb_grid)
|
||||
{
|
||||
VolumeGridVector &grids = *volume.runtime.grids;
|
||||
BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr);
|
||||
BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN);
|
||||
|
||||
vdb_grid->setName(name);
|
||||
grids.emplace_back(vdb_grid);
|
||||
return &grids.back();
|
||||
}
|
||||
#endif
|
||||
|
||||
void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid)
|
||||
{
|
||||
#ifdef WITH_OPENVDB
|
||||
|
|
|
@ -121,46 +121,66 @@ struct VolumeToMeshOp {
|
|||
}
|
||||
};
|
||||
|
||||
static Mesh *new_mesh_from_openvdb_data(Span<openvdb::Vec3s> verts,
|
||||
Span<openvdb::Vec3I> tris,
|
||||
Span<openvdb::Vec4I> quads)
|
||||
/**
|
||||
* Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure.
|
||||
* This can be used to add mesh data from a grid into an existing mesh rather than merging multiple
|
||||
* meshes later on.
|
||||
*/
|
||||
void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts,
|
||||
const Span<openvdb::Vec3I> vdb_tris,
|
||||
const Span<openvdb::Vec4I> vdb_quads,
|
||||
const int vert_offset,
|
||||
const int poly_offset,
|
||||
const int loop_offset,
|
||||
MutableSpan<MVert> verts,
|
||||
MutableSpan<MPoly> polys,
|
||||
MutableSpan<MLoop> loops)
|
||||
{
|
||||
const int tot_loops = 3 * tris.size() + 4 * quads.size();
|
||||
const int tot_polys = tris.size() + quads.size();
|
||||
|
||||
Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys);
|
||||
|
||||
/* Write vertices. */
|
||||
for (const int i : verts.index_range()) {
|
||||
const blender::float3 co = blender::float3(verts[i].asV());
|
||||
copy_v3_v3(mesh->mvert[i].co, co);
|
||||
for (const int i : vdb_verts.index_range()) {
|
||||
const blender::float3 co = blender::float3(vdb_verts[i].asV());
|
||||
copy_v3_v3(verts[vert_offset + i].co, co);
|
||||
}
|
||||
|
||||
/* Write triangles. */
|
||||
for (const int i : tris.index_range()) {
|
||||
mesh->mpoly[i].loopstart = 3 * i;
|
||||
mesh->mpoly[i].totloop = 3;
|
||||
for (const int i : vdb_tris.index_range()) {
|
||||
polys[poly_offset + i].loopstart = loop_offset + 3 * i;
|
||||
polys[poly_offset + i].totloop = 3;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
/* Reverse vertex order to get correct normals. */
|
||||
mesh->mloop[3 * i + j].v = tris[i][2 - j];
|
||||
loops[loop_offset + 3 * i + j].v = vert_offset + vdb_tris[i][2 - j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Write quads. */
|
||||
const int poly_offset = tris.size();
|
||||
const int loop_offset = tris.size() * 3;
|
||||
for (const int i : quads.index_range()) {
|
||||
mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i;
|
||||
mesh->mpoly[poly_offset + i].totloop = 4;
|
||||
const int quad_offset = poly_offset + vdb_tris.size();
|
||||
const int quad_loop_offset = loop_offset + vdb_tris.size() * 3;
|
||||
for (const int i : vdb_quads.index_range()) {
|
||||
polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i;
|
||||
polys[quad_offset + i].totloop = 4;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
/* Reverse vertex order to get correct normals. */
|
||||
mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j];
|
||||
loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_mesh_calc_edges(mesh, false, false);
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
return mesh;
|
||||
/**
|
||||
* Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and
|
||||
* triangle indices.
|
||||
*/
|
||||
bke::OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid,
|
||||
const VolumeToMeshResolution &resolution,
|
||||
const float threshold,
|
||||
const float adaptivity)
|
||||
{
|
||||
const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
|
||||
|
||||
VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity};
|
||||
if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) {
|
||||
return {};
|
||||
}
|
||||
return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)};
|
||||
}
|
||||
|
||||
Mesh *volume_to_mesh(const openvdb::GridBase &grid,
|
||||
|
@ -168,14 +188,27 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid,
|
|||
const float threshold,
|
||||
const float adaptivity)
|
||||
{
|
||||
const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
|
||||
const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data(
|
||||
grid, resolution, threshold, adaptivity);
|
||||
|
||||
VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity};
|
||||
if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) {
|
||||
return nullptr;
|
||||
}
|
||||
const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size();
|
||||
const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size();
|
||||
Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys);
|
||||
|
||||
return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads);
|
||||
fill_mesh_from_openvdb_data(mesh_data.verts,
|
||||
mesh_data.tris,
|
||||
mesh_data.quads,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
{mesh->mvert, mesh->totvert},
|
||||
{mesh->mpoly, mesh->totpoly},
|
||||
{mesh->mloop, mesh->totloop});
|
||||
|
||||
BKE_mesh_calc_edges(mesh, false, false);
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
#endif /* WITH_OPENVDB */
|
||||
|
|
|
@ -1985,7 +1985,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) {
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density");
|
||||
version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density");
|
||||
}
|
||||
}
|
||||
FOREACH_NODETREE_END;
|
||||
|
|
|
@ -967,7 +967,7 @@ static bool geometry_node_is_293_legacy(const short node_type)
|
|||
|
||||
/* Maybe legacy: Special case for grid names? Or finish patch from level set branch to
|
||||
* generate a mesh for all grids in the volume. */
|
||||
case GEO_NODE_VOLUME_TO_MESH:
|
||||
case GEO_NODE_LEGACY_VOLUME_TO_MESH:
|
||||
return false;
|
||||
|
||||
/* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */
|
||||
|
@ -2098,6 +2098,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString");
|
||||
version_geometry_nodes_set_position_node_offset(ntree);
|
||||
version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh");
|
||||
}
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
|
|
|
@ -193,6 +193,8 @@ set(SRC
|
|||
geometry/nodes/legacy/node_geo_raycast.cc
|
||||
geometry/nodes/legacy/node_geo_select_by_material.cc
|
||||
geometry/nodes/legacy/node_geo_subdivision_surface.cc
|
||||
geometry/nodes/legacy/node_geo_volume_to_mesh.cc
|
||||
|
||||
geometry/nodes/node_geo_attribute_capture.cc
|
||||
geometry/nodes/node_geo_attribute_remove.cc
|
||||
geometry/nodes/node_geo_attribute_statistic.cc
|
||||
|
|
|
@ -47,6 +47,7 @@ void register_node_type_geo_legacy_curve_subdivide(void);
|
|||
void register_node_type_geo_legacy_edge_split(void);
|
||||
void register_node_type_geo_legacy_subdivision_surface(void);
|
||||
void register_node_type_geo_legacy_raycast(void);
|
||||
void register_node_type_geo_legacy_volume_to_mesh(void);
|
||||
|
||||
void register_node_type_geo_align_rotation_to_vector(void);
|
||||
void register_node_type_geo_attribute_capture(void);
|
||||
|
|
|
@ -321,6 +321,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to
|
|||
DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "")
|
||||
DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "")
|
||||
DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "")
|
||||
DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "")
|
||||
|
||||
DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
#ifdef WITH_OPENVDB
|
||||
# include <openvdb/tools/GridTransformer.h>
|
||||
# include <openvdb/tools/VolumeToMesh.h>
|
||||
#endif
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
#include "BKE_volume.h"
|
||||
#include "BKE_volume_to_mesh.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>("Geometry");
|
||||
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 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>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
|
||||
b.add_output<decl::Geometry>("Geometry");
|
||||
}
|
||||
|
||||
static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN(
|
||||
sizeof(NodeGeometryVolumeToMesh), __func__);
|
||||
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;
|
||||
}
|
||||
|
||||
static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage;
|
||||
|
||||
bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
|
||||
bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
|
||||
nodeSetSocketAvailability(voxel_amount_socket,
|
||||
data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT);
|
||||
nodeSetSocketAvailability(voxel_size_socket,
|
||||
data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE);
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
static void create_mesh_from_volume(GeometrySet &geometry_set_in,
|
||||
GeometrySet &geometry_set_out,
|
||||
GeoNodeExecParams ¶ms)
|
||||
{
|
||||
if (!geometry_set_in.has<VolumeComponent>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NodeGeometryVolumeToMesh &storage =
|
||||
*(const NodeGeometryVolumeToMesh *)params.node().storage;
|
||||
|
||||
bke::VolumeToMeshResolution resolution;
|
||||
resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode;
|
||||
if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) {
|
||||
resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
|
||||
if (resolution.settings.voxel_amount <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
|
||||
resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
|
||||
if (resolution.settings.voxel_size <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>();
|
||||
const Volume *volume = component->get_for_read();
|
||||
if (volume == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Main *bmain = DEG_get_bmain(params.depsgraph());
|
||||
BKE_volume_load(volume, bmain);
|
||||
|
||||
const std::string grid_name = params.get_input<std::string>("Density");
|
||||
const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
|
||||
if (volume_grid == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
float threshold = params.get_input<float>("Threshold");
|
||||
float adaptivity = params.get_input<float>("Adaptivity");
|
||||
|
||||
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>();
|
||||
dst_component.replace(mesh);
|
||||
}
|
||||
|
||||
#endif /* WITH_OPENVDB */
|
||||
|
||||
static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
|
||||
GeometrySet geometry_set_out;
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
create_mesh_from_volume(geometry_set_in, geometry_set_out, params);
|
||||
#else
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Disabled, Blender was compiled without OpenVDB"));
|
||||
#endif
|
||||
|
||||
params.set_output("Geometry", geometry_set_out);
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
||||
void register_node_type_geo_legacy_volume_to_mesh()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
geo_node_type_base(
|
||||
&ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0);
|
||||
ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare;
|
||||
node_type_storage(
|
||||
&ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_size(&ntype, 170, 120, 700);
|
||||
node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init);
|
||||
node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec;
|
||||
ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
|
@ -39,13 +39,12 @@ namespace blender::nodes {
|
|||
|
||||
static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Geometry>("Geometry");
|
||||
b.add_input<decl::String>("Density");
|
||||
b.add_input<decl::Geometry>("Volume");
|
||||
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>("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_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)
|
||||
|
@ -60,11 +59,6 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
|
|||
NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN(
|
||||
sizeof(NodeGeometryVolumeToMesh), __func__);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -82,75 +76,120 @@ static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node
|
|||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
static void create_mesh_from_volume(GeometrySet &geometry_set_in,
|
||||
GeometrySet &geometry_set_out,
|
||||
GeoNodeExecParams ¶ms)
|
||||
static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms)
|
||||
{
|
||||
if (!geometry_set_in.has<VolumeComponent>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NodeGeometryVolumeToMesh &storage =
|
||||
*(const NodeGeometryVolumeToMesh *)params.node().storage;
|
||||
|
||||
bke::VolumeToMeshResolution resolution;
|
||||
resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode;
|
||||
if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) {
|
||||
resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
|
||||
if (resolution.settings.voxel_amount <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f);
|
||||
}
|
||||
else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
|
||||
resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
|
||||
if (resolution.settings.voxel_size <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f);
|
||||
}
|
||||
|
||||
const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>();
|
||||
const Volume *volume = component->get_for_read();
|
||||
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());
|
||||
}
|
||||
|
||||
Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset);
|
||||
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) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bke::VolumeToMeshResolution resolution = get_resolution_param(params);
|
||||
const Main *bmain = DEG_get_bmain(params.depsgraph());
|
||||
BKE_volume_load(volume, bmain);
|
||||
|
||||
const std::string grid_name = params.get_input<std::string>("Density");
|
||||
const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
|
||||
if (volume_grid == nullptr) {
|
||||
return;
|
||||
Vector<openvdb::GridBase::ConstPtr> grids;
|
||||
for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
|
||||
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
|
||||
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");
|
||||
float adaptivity = params.get_input<float>("Adaptivity");
|
||||
|
||||
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;
|
||||
if (grids.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
BKE_id_material_eval_ensure_default_slot(&mesh->id);
|
||||
MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
|
||||
dst_component.replace(mesh);
|
||||
|
||||
return create_mesh_from_volume_grids(grids,
|
||||
params.get_input<float>("Threshold"),
|
||||
params.get_input<float>("Adaptivity"),
|
||||
resolution);
|
||||
}
|
||||
|
||||
#endif /* WITH_OPENVDB */
|
||||
|
||||
static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
|
||||
GeometrySet geometry_set_out;
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
|
||||
|
||||
#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});
|
||||
});
|
||||
#else
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Disabled, Blender was compiled without OpenVDB"));
|
||||
#endif
|
||||
|
||||
params.set_output("Geometry", geometry_set_out);
|
||||
params.set_output("Mesh", std::move(geometry_set));
|
||||
}
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
|
Loading…
Reference in New Issue