2020-09-29 16:02:40 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup modifiers
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "BKE_lib_query.h"
|
|
|
|
#include "BKE_mesh_runtime.h"
|
2020-10-01 15:53:50 +02:00
|
|
|
#include "BKE_mesh_wrapper.h"
|
2020-09-29 16:02:40 +02:00
|
|
|
#include "BKE_modifier.h"
|
|
|
|
#include "BKE_object.h"
|
|
|
|
#include "BKE_volume.h"
|
|
|
|
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "DNA_volume_types.h"
|
|
|
|
|
2020-10-20 11:00:16 +02:00
|
|
|
#include "DEG_depsgraph.h"
|
2020-09-29 16:02:40 +02:00
|
|
|
|
|
|
|
#include "UI_interface.h"
|
|
|
|
#include "UI_resources.h"
|
|
|
|
|
|
|
|
#include "BLO_read_write.h"
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "MOD_modifiertypes.h"
|
|
|
|
#include "MOD_ui_common.h"
|
|
|
|
|
|
|
|
#include "BLI_float4x4.hh"
|
|
|
|
#include "BLI_index_range.hh"
|
|
|
|
#include "BLI_span.hh"
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
|
|
|
|
#ifdef WITH_OPENVDB
|
|
|
|
# include <openvdb/openvdb.h>
|
|
|
|
# include <openvdb/tools/MeshToVolume.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef WITH_OPENVDB
|
|
|
|
namespace blender {
|
|
|
|
/* This class follows the MeshDataAdapter interface from openvdb. */
|
|
|
|
class OpenVDBMeshAdapter {
|
|
|
|
private:
|
|
|
|
Span<MVert> vertices_;
|
|
|
|
Span<MLoop> loops_;
|
|
|
|
Span<MLoopTri> looptris_;
|
|
|
|
float4x4 transform_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
OpenVDBMeshAdapter(Mesh &mesh, float4x4 transform)
|
|
|
|
: vertices_(mesh.mvert, mesh.totvert),
|
|
|
|
loops_(mesh.mloop, mesh.totloop),
|
|
|
|
transform_(transform)
|
|
|
|
{
|
|
|
|
const MLoopTri *looptries = BKE_mesh_runtime_looptri_ensure(&mesh);
|
|
|
|
const int looptries_len = BKE_mesh_runtime_looptri_len(&mesh);
|
|
|
|
looptris_ = Span(looptries, looptries_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t polygonCount() const
|
|
|
|
{
|
|
|
|
return static_cast<size_t>(looptris_.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t pointCount() const
|
|
|
|
{
|
|
|
|
return static_cast<size_t>(vertices_.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t vertexCount(size_t UNUSED(polygon_index)) const
|
|
|
|
{
|
|
|
|
/* All polygons are triangles. */
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
void getIndexSpacePoint(size_t polygon_index, size_t vertex_index, openvdb::Vec3d &pos) const
|
|
|
|
{
|
|
|
|
const MLoopTri &looptri = looptris_[polygon_index];
|
|
|
|
const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v];
|
|
|
|
const float3 transformed_co = transform_ * float3(vertex.co);
|
|
|
|
pos = &transformed_co.x;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace blender
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void initData(ModifierData *md)
|
|
|
|
{
|
|
|
|
MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md);
|
|
|
|
mvmd->object = NULL;
|
|
|
|
mvmd->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT;
|
|
|
|
mvmd->voxel_size = 0.1f;
|
|
|
|
mvmd->voxel_amount = 32;
|
|
|
|
mvmd->fill_volume = true;
|
|
|
|
mvmd->interior_band_width = 0.1f;
|
|
|
|
mvmd->exterior_band_width = 0.1f;
|
|
|
|
mvmd->density = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
|
|
|
|
{
|
|
|
|
MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md);
|
2020-10-05 11:20:59 +02:00
|
|
|
DEG_add_modifier_to_transform_relation(ctx->node, "Mesh to Volume Modifier");
|
2020-09-29 16:02:40 +02:00
|
|
|
if (mvmd->object) {
|
|
|
|
DEG_add_object_relation(
|
|
|
|
ctx->node, mvmd->object, DEG_OB_COMP_GEOMETRY, "Mesh to Volume Modifier");
|
|
|
|
DEG_add_object_relation(
|
|
|
|
ctx->node, mvmd->object, DEG_OB_COMP_TRANSFORM, "Mesh to Volume Modifier");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-01 18:05:23 +02:00
|
|
|
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
|
2020-09-29 16:02:40 +02:00
|
|
|
{
|
|
|
|
MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md);
|
2020-10-01 18:05:23 +02:00
|
|
|
walk(userData, ob, (ID **)&mvmd->object, IDWALK_CB_NOP);
|
2020-09-29 16:02:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|
|
|
{
|
|
|
|
uiLayout *layout = panel->layout;
|
|
|
|
|
2020-10-01 09:05:29 -05:00
|
|
|
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
|
2020-09-29 16:02:40 +02:00
|
|
|
MeshToVolumeModifierData *mvmd = static_cast<MeshToVolumeModifierData *>(ptr->data);
|
|
|
|
|
|
|
|
uiLayoutSetPropSep(layout, true);
|
|
|
|
uiLayoutSetPropDecorate(layout, false);
|
|
|
|
|
|
|
|
uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
|
|
|
|
uiItemR(layout, ptr, "density", 0, NULL, ICON_NONE);
|
|
|
|
|
|
|
|
{
|
|
|
|
uiLayout *col = uiLayoutColumn(layout, false);
|
2020-09-30 15:01:12 +10:00
|
|
|
uiItemR(col, ptr, "use_fill_volume", 0, NULL, ICON_NONE);
|
2020-09-29 16:02:40 +02:00
|
|
|
uiItemR(col, ptr, "exterior_band_width", 0, NULL, ICON_NONE);
|
|
|
|
|
|
|
|
uiLayout *subcol = uiLayoutColumn(col, false);
|
2020-10-01 12:13:38 +02:00
|
|
|
uiLayoutSetActive(subcol, !mvmd->fill_volume);
|
2020-09-29 16:02:40 +02:00
|
|
|
uiItemR(subcol, ptr, "interior_band_width", 0, NULL, ICON_NONE);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
uiLayout *col = uiLayoutColumn(layout, false);
|
|
|
|
uiItemR(col, ptr, "resolution_mode", 0, NULL, ICON_NONE);
|
|
|
|
if (mvmd->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) {
|
|
|
|
uiItemR(col, ptr, "voxel_amount", 0, NULL, ICON_NONE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
uiItemR(col, ptr, "voxel_size", 0, NULL, ICON_NONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
modifier_panel_end(layout, ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void panelRegister(ARegionType *region_type)
|
|
|
|
{
|
|
|
|
modifier_panel_register(region_type, eModifierType_MeshToVolume, panel_draw);
|
|
|
|
}
|
|
|
|
|
2020-10-20 17:50:08 +02:00
|
|
|
#ifdef WITH_OPENVDB
|
2020-10-20 11:00:16 +02:00
|
|
|
static float compute_voxel_size(const ModifierEvalContext *ctx,
|
|
|
|
const MeshToVolumeModifierData *mvmd,
|
2020-09-29 16:02:40 +02:00
|
|
|
const blender::float4x4 &transform)
|
|
|
|
{
|
|
|
|
using namespace blender;
|
2020-10-20 11:00:16 +02:00
|
|
|
|
|
|
|
float volume_simplify = BKE_volume_simplify_factor(ctx->depsgraph);
|
|
|
|
if (volume_simplify == 0.0f) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
|
2020-09-29 16:02:40 +02:00
|
|
|
if (mvmd->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
|
2020-10-20 11:00:16 +02:00
|
|
|
return mvmd->voxel_size / volume_simplify;
|
2020-10-09 12:55:13 +02:00
|
|
|
}
|
|
|
|
if (mvmd->voxel_amount <= 0) {
|
|
|
|
return 0;
|
2020-09-29 16:02:40 +02:00
|
|
|
}
|
|
|
|
/* Compute the voxel size based on the desired number of voxels and the approximated bounding box
|
|
|
|
* of the volume. */
|
|
|
|
const BoundBox *bb = BKE_object_boundbox_get(mvmd->object);
|
2020-10-12 12:13:01 +02:00
|
|
|
const float diagonal = float3::distance(transform * float3(bb->vec[6]),
|
|
|
|
transform * float3(bb->vec[0]));
|
|
|
|
const float approximate_volume_side_length = diagonal + mvmd->exterior_band_width * 2.0f;
|
2020-10-20 11:00:16 +02:00
|
|
|
const float voxel_size = approximate_volume_side_length / mvmd->voxel_amount / volume_simplify;
|
2020-09-29 16:02:40 +02:00
|
|
|
return voxel_size;
|
|
|
|
}
|
2020-10-20 17:50:08 +02:00
|
|
|
#endif
|
2020-09-29 16:02:40 +02:00
|
|
|
|
2020-10-05 11:20:59 +02:00
|
|
|
static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume)
|
2020-09-29 16:02:40 +02:00
|
|
|
{
|
|
|
|
#ifdef WITH_OPENVDB
|
|
|
|
using namespace blender;
|
|
|
|
|
|
|
|
MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md);
|
|
|
|
Object *object_to_convert = mvmd->object;
|
|
|
|
|
|
|
|
if (object_to_convert == NULL) {
|
|
|
|
return input_volume;
|
|
|
|
}
|
|
|
|
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_to_convert, false);
|
|
|
|
if (mesh == NULL) {
|
|
|
|
return input_volume;
|
|
|
|
}
|
2020-10-01 15:53:50 +02:00
|
|
|
BKE_mesh_wrapper_ensure_mdata(mesh);
|
2020-09-29 16:02:40 +02:00
|
|
|
|
2020-10-05 11:20:59 +02:00
|
|
|
const float4x4 mesh_to_own_object_space_transform = float4x4(ctx->object->imat) *
|
|
|
|
float4x4(object_to_convert->obmat);
|
2020-10-20 11:00:16 +02:00
|
|
|
const float voxel_size = compute_voxel_size(ctx, mvmd, mesh_to_own_object_space_transform);
|
2020-10-09 12:55:13 +02:00
|
|
|
if (voxel_size == 0.0f) {
|
|
|
|
return input_volume;
|
|
|
|
}
|
2020-09-29 16:02:40 +02:00
|
|
|
|
|
|
|
float4x4 mesh_to_index_space_transform;
|
|
|
|
scale_m4_fl(mesh_to_index_space_transform.values, 1.0f / voxel_size);
|
|
|
|
mul_m4_m4_post(mesh_to_index_space_transform.values, mesh_to_own_object_space_transform.values);
|
|
|
|
/* Better align generated grid with the source mesh. */
|
|
|
|
add_v3_fl(mesh_to_index_space_transform.values[3], -0.5f);
|
|
|
|
|
|
|
|
OpenVDBMeshAdapter mesh_adapter{*mesh, mesh_to_index_space_transform};
|
|
|
|
|
|
|
|
/* Convert the bandwidths from object in index space. */
|
|
|
|
const float exterior_band_width = MAX2(0.001f, mvmd->exterior_band_width / voxel_size);
|
|
|
|
const float interior_band_width = MAX2(0.001f, mvmd->interior_band_width / voxel_size);
|
|
|
|
|
|
|
|
openvdb::FloatGrid::Ptr new_grid;
|
|
|
|
if (mvmd->fill_volume) {
|
|
|
|
/* Setting the interior bandwidth to FLT_MAX, will make it fill the entire volume. */
|
|
|
|
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
|
|
|
mesh_adapter, {}, exterior_band_width, FLT_MAX);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
new_grid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
|
|
|
mesh_adapter, {}, exterior_band_width, interior_band_width);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a new volume object and add the density grid. */
|
|
|
|
Volume *volume = BKE_volume_new_for_eval(input_volume);
|
|
|
|
VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT);
|
2020-10-20 11:00:16 +02:00
|
|
|
openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>(
|
|
|
|
BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false));
|
2020-09-29 16:02:40 +02:00
|
|
|
|
|
|
|
/* Merge the generated grid into the density grid. Should be cheap because density_grid has just
|
|
|
|
* been created as well. */
|
|
|
|
density_grid->merge(*new_grid);
|
|
|
|
|
|
|
|
/* Change transform so that the index space is correctly transformed to object space. */
|
|
|
|
density_grid->transform().postScale(voxel_size);
|
|
|
|
|
|
|
|
/* Give each grid cell a fixed density for now. */
|
|
|
|
openvdb::tools::foreach (
|
|
|
|
density_grid->beginValueOn(),
|
|
|
|
[&](const openvdb::FloatGrid::ValueOnIter &iter) { iter.setValue(mvmd->density); });
|
|
|
|
|
|
|
|
return volume;
|
|
|
|
|
|
|
|
#else
|
2020-10-05 11:20:59 +02:00
|
|
|
UNUSED_VARS(md, ctx);
|
2020-09-29 16:02:40 +02:00
|
|
|
BKE_modifier_set_error(md, "Compiled without OpenVDB");
|
|
|
|
return input_volume;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
ModifierTypeInfo modifierType_MeshToVolume = {
|
|
|
|
/* name */ "Mesh to Volume",
|
|
|
|
/* structName */ "MeshToVolumeModifierData",
|
|
|
|
/* structSize */ sizeof(MeshToVolumeModifierData),
|
|
|
|
/* srna */ &RNA_MeshToVolumeModifier,
|
|
|
|
/* type */ eModifierTypeType_Constructive,
|
|
|
|
/* flags */ static_cast<ModifierTypeFlag>(0),
|
|
|
|
/* icon */ ICON_VOLUME_DATA, /* TODO: Use correct icon. */
|
|
|
|
|
|
|
|
/* copyData */ BKE_modifier_copydata_generic,
|
|
|
|
|
|
|
|
/* deformVerts */ NULL,
|
|
|
|
/* deformMatrices */ NULL,
|
|
|
|
/* deformVertsEM */ NULL,
|
|
|
|
/* deformMatricesEM */ NULL,
|
|
|
|
/* modifyMesh */ NULL,
|
|
|
|
/* modifyHair */ NULL,
|
|
|
|
/* modifyPointCloud */ NULL,
|
|
|
|
/* modifyVolume */ modifyVolume,
|
|
|
|
|
|
|
|
/* initData */ initData,
|
|
|
|
/* requiredDataMask */ NULL,
|
|
|
|
/* freeData */ NULL,
|
|
|
|
/* isDisabled */ NULL,
|
|
|
|
/* updateDepsgraph */ updateDepsgraph,
|
|
|
|
/* dependsOnTime */ NULL,
|
|
|
|
/* dependsOnNormals */ NULL,
|
2020-10-01 18:05:23 +02:00
|
|
|
/* foreachIDLink */ foreachIDLink,
|
2020-09-29 16:02:40 +02:00
|
|
|
/* foreachTexLink */ NULL,
|
|
|
|
/* freeRuntimeData */ NULL,
|
|
|
|
/* panelRegister */ panelRegister,
|
|
|
|
/* blendWrite */ NULL,
|
|
|
|
/* blendRead */ NULL,
|
|
|
|
};
|