Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f21c45e755 | ||
|
|
52c83ac3a1 |
@@ -234,10 +234,17 @@ struct rt_bot_repair_info {
|
||||
fastf_t max_hole_area_percent;
|
||||
int strict;
|
||||
|
||||
/* OpenVDB repair controls. When force_openvdb is zero OpenVDB is used
|
||||
* only as a fallback after the conventional repair path fails. */
|
||||
int force_openvdb;
|
||||
fastf_t openvdb_voxel_size;
|
||||
|
||||
// Info about generated output
|
||||
int output_nonmanifold;
|
||||
int output_lint_fail;
|
||||
fastf_t output_volume;
|
||||
int openvdb_available;
|
||||
int used_openvdb;
|
||||
unsigned int output_data_loss;
|
||||
};
|
||||
|
||||
@@ -252,7 +259,7 @@ struct rt_bot_repair_info {
|
||||
* tests. This isn't always desirable - sometimes manifold is enough even if
|
||||
* the mesh is not otherwise well behaved - so it is an user settable param.
|
||||
*/
|
||||
#define RT_BOT_REPAIR_INFO_INIT {0.0, 5.0, 1, 0, 0, 0.0, 0};
|
||||
#define RT_BOT_REPAIR_INFO_INIT {0.0, 5.0, 1, 0, 0.0, 0, 0, 0.0, 0, 0, 0};
|
||||
|
||||
/* Function to attempt repairing a non-manifold BoT. Returns 1 if ibot was
|
||||
* already manifold (obot will contain NULL), 0 if a manifold BoT was created
|
||||
|
||||
+15
-10
@@ -26,11 +26,10 @@
|
||||
#include "bu/app.h"
|
||||
#include "bu/malloc.h"
|
||||
#include "bu/vfont.h"
|
||||
#include "bu/vls.h"
|
||||
|
||||
#define FONTDIR2 "/usr/lib/vfont"
|
||||
#define DEFAULT_FONT "nonie.r.12"
|
||||
#define FONTNAMESZ 128
|
||||
|
||||
#include "./vfont.h"
|
||||
|
||||
|
||||
@@ -127,7 +126,7 @@ vfont_get(char *font)
|
||||
register struct vfont *vfp = VFONT_NULL;
|
||||
register FILE *fp = NULL;
|
||||
register int i;
|
||||
char fname[FONTNAMESZ] = {'\0'};
|
||||
struct bu_vls fname = BU_VLS_INIT_ZERO;
|
||||
unsigned char header[2*5]; /* 5 16-bit vax shorts */
|
||||
unsigned char dispatch[10*256]; /* 256 10-byte structs */
|
||||
uint16_t magic;
|
||||
@@ -135,21 +134,24 @@ vfont_get(char *font)
|
||||
const char *const_font;
|
||||
|
||||
const_font = (font == NULL) ? DEFAULT_FONT : (const char *)font;
|
||||
bu_vls_strcpy(&fname, const_font);
|
||||
|
||||
/* Open the file and read in the header information. */
|
||||
if ((fp = fopen(const_font, "rb")) == NULL) {
|
||||
snprintf(fname, FONTNAMESZ, "%s/%s", bu_dir(NULL, 0, BU_DIR_DATA, "vfont", NULL), const_font);
|
||||
if ((fp = fopen(fname, "rb")) == NULL) {
|
||||
snprintf(fname, FONTNAMESZ, "%s/%s", FONTDIR2, const_font);
|
||||
if ((fp = fopen(fname, "rb")) == NULL) {
|
||||
bu_vls_sprintf(&fname, "%s/%s", bu_dir(NULL, 0, BU_DIR_DATA, "vfont", NULL), const_font);
|
||||
if ((fp = fopen(bu_vls_addr(&fname), "rb")) == NULL) {
|
||||
bu_vls_sprintf(&fname, "%s/%s", FONTDIR2, const_font);
|
||||
if ((fp = fopen(bu_vls_addr(&fname), "rb")) == NULL) {
|
||||
bu_vls_free(&fname);
|
||||
return VFONT_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fread((char *)header, sizeof(header), 1, fp) != 1 ||
|
||||
fread((char *)dispatch, sizeof(dispatch), 1, fp) != 1) {
|
||||
fprintf(stderr, "vfont_get(%s): header read error\n", fname);
|
||||
fprintf(stderr, "vfont_get(%s): header read error\n", bu_vls_addr(&fname));
|
||||
fclose(fp);
|
||||
bu_vls_free(&fname);
|
||||
return VFONT_NULL;
|
||||
}
|
||||
magic = _vax_gshort(&header[0*2]) & 0xFFFF;
|
||||
@@ -157,8 +159,9 @@ vfont_get(char *font)
|
||||
|
||||
if (UNLIKELY(magic != 0436)) {
|
||||
fprintf(stderr, "vfont_get(%s): bad magic number 0%o\n",
|
||||
fname, magic);
|
||||
bu_vls_addr(&fname), magic);
|
||||
fclose(fp);
|
||||
bu_vls_free(&fname);
|
||||
return VFONT_NULL;
|
||||
}
|
||||
|
||||
@@ -166,10 +169,11 @@ vfont_get(char *font)
|
||||
BU_ALLOC(vfp, struct vfont);
|
||||
vfp->vf_bits = (char *)bu_malloc((size_t)size, "vfont bits");
|
||||
if (fread(vfp->vf_bits, (size_t)size, 1, fp) != 1) {
|
||||
fprintf(stderr, "vfont_get(%s): bitmap read error\n", fname);
|
||||
fprintf(stderr, "vfont_get(%s): bitmap read error\n", bu_vls_addr(&fname));
|
||||
fclose(fp);
|
||||
bu_free(vfp->vf_bits, "vfont bits");
|
||||
bu_free((char *)vfp, "vfont");
|
||||
bu_vls_free(&fname);
|
||||
return VFONT_NULL;
|
||||
}
|
||||
|
||||
@@ -194,6 +198,7 @@ vfont_get(char *font)
|
||||
vdp->vd_width = _vax_gshort(&cp[8]);
|
||||
}
|
||||
fclose(fp);
|
||||
bu_vls_free(&fname);
|
||||
return vfp;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@ brlcad_find_package(manifold REQUIRED)
|
||||
BRLCAD_Find_Package(TinyGLTF REQUIRED)
|
||||
|
||||
set(BOT_LIBS libged libbg libbu manifold::manifold tinygltf::tinygltf ${WINSOCK_LIB})
|
||||
set(BOT_INTERNAL_INCLUDE_DIRS)
|
||||
|
||||
|
||||
if(BRLCAD_ENABLE_OPENVDB)
|
||||
set(BOT_LIBS ${BOT_LIBS} ${OpenVDB_LIBRARIES} ${IlmBase_LIBRARIES} ${Tbb_LIBRARIES})
|
||||
set(BOT_LOCAL_INCLUDE_DIRS ${BOT_LOCAL_INCLUDE_DIRS} ${OpenVDB_INCLUDE_DIRS} ${Tbb_INCLUDE_DIRS} ${IlmBase_INCLUDE_DIRS})
|
||||
list(APPEND BOT_INTERNAL_INCLUDE_DIRS ${BRLCAD_SOURCE_DIR}/src/librt/primitives/bot)
|
||||
set(BOT_OPENVDB_DEFS HAVE_OPENVDB_BOT_EXTERIOR BRLCAD_OPENVDB)
|
||||
endif(BRLCAD_ENABLE_OPENVDB)
|
||||
|
||||
@@ -54,11 +56,11 @@ if(BRLCAD_ENABLE_OPENVDB)
|
||||
# PLUGIN_ONLY these flags are scoped to the plugin .so and do not
|
||||
# pollute the main libged target.
|
||||
set_source_files_properties(
|
||||
remesh.cpp
|
||||
bot_flood_exterior.cpp
|
||||
bot_openvdb.cpp
|
||||
PROPERTIES COMPILE_FLAGS "-w -fno-lto"
|
||||
)
|
||||
list(APPEND BOT_SRCS bot_openvdb.cpp bot_flood_exterior.cpp)
|
||||
list(APPEND BOT_SRCS bot_flood_exterior.cpp)
|
||||
endif(BRLCAD_ENABLE_OPENVDB)
|
||||
|
||||
set(
|
||||
@@ -78,7 +80,7 @@ set(
|
||||
ged_plugin_library(ged-bot
|
||||
PLUGIN_ONLY
|
||||
${BOT_SRCS}
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${BOT_INTERNAL_INCLUDE_DIRS}
|
||||
SYSTEM_INCLUDES ${BOT_LOCAL_INCLUDE_DIRS}
|
||||
LIBS ${BOT_LIBS}
|
||||
DEFINITIONS "EIGEN_DONT_VECTORIZE" ${BOT_OPENVDB_DEFS}
|
||||
@@ -87,8 +89,6 @@ ged_plugin_library(ged-bot
|
||||
cmakefiles(
|
||||
CMakeLists.txt
|
||||
bot_flood_exterior.cpp
|
||||
bot_openvdb.cpp
|
||||
bot_openvdb.h
|
||||
concurrentqueue.h
|
||||
dump/ged_bot_dump.h
|
||||
ged_bot.h
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
* Algorithm
|
||||
* ---------
|
||||
* 1. Build an occupancy grid (solid / air) via rt_rtip_to_occupancy_grid
|
||||
* 1. Build an occupancy grid (solid / air) via rt_rtip_to_openvdb_occupancy
|
||||
* (implemented in bot_openvdb.cpp). Rays are shot in +X, +Y, +Z.
|
||||
* A voxel is marked solid when any hit segment encloses its centre.
|
||||
*
|
||||
@@ -143,7 +143,7 @@ bot_flood_exterior_classify(struct rt_i *rtip,
|
||||
|
||||
/* Step 1: voxelise the model into a solid-occupancy grid. */
|
||||
bu_log("bot flood exterior: voxel_size=%.4g\n", voxel_size);
|
||||
openvdb::BoolGrid::Ptr solid = rt_rtip_to_occupancy_grid(rtip, voxel_size, &nx, &ny, &nz);
|
||||
openvdb::BoolGrid::Ptr solid = rt_rtip_to_openvdb_occupancy(rtip, voxel_size, &nx, &ny, &nz);
|
||||
bu_log("bot flood exterior: grid %dx%dx%d\n", nx, ny, nz);
|
||||
|
||||
/* Step 2: BFS flood fill to find exterior (water-reachable) voxels. */
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
* eliminating the need for a separate global sampling phase.
|
||||
*
|
||||
* Flood fill (--flood-fill, requires OpenVDB)
|
||||
* The model is voxelised via rt_rtip_to_occupancy_grid, then a
|
||||
* The model is voxelised via rt_rtip_to_openvdb_occupancy, then a
|
||||
* BFS flood-fill from the boundary finds all "water-reachable" voxels.
|
||||
* A face is exterior when any of its six face-adjacent voxels is
|
||||
* reachable.
|
||||
|
||||
+91
-152
@@ -25,11 +25,9 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef OPENVDB_ABI_VERSION_NUMBER
|
||||
# include <openvdb/openvdb.h>
|
||||
# include <openvdb/tools/VolumeToMesh.h>
|
||||
# include <openvdb/tools/MeshToVolume.h>
|
||||
#endif /* OPENVDB_ABI_VERSION_NUMBER */
|
||||
#ifdef BRLCAD_OPENVDB
|
||||
# include "bot_openvdb.h"
|
||||
#endif /* BRLCAD_OPENVDB */
|
||||
|
||||
#include "manifold/manifold.h"
|
||||
|
||||
@@ -49,115 +47,52 @@
|
||||
#include "./ged_bot.h"
|
||||
|
||||
|
||||
#ifdef OPENVDB_ABI_VERSION_NUMBER
|
||||
|
||||
struct botDataAdapter {
|
||||
struct rt_bot_internal *bot;
|
||||
|
||||
size_t polygonCount() const {
|
||||
return bot->num_faces;
|
||||
};
|
||||
size_t pointCount() const {
|
||||
return bot->num_vertices;
|
||||
};
|
||||
size_t vertexCount(size_t) const {
|
||||
return 3;
|
||||
};
|
||||
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const {
|
||||
int idx = bot->faces[(n*3)+v];
|
||||
pos[X] = bot->vertices[(idx*3)+X];
|
||||
pos[Y] = bot->vertices[(idx*3)+Y];
|
||||
pos[Z] = bot->vertices[(idx*3)+Z];
|
||||
return;
|
||||
};
|
||||
|
||||
/* constructor */
|
||||
botDataAdapter(struct rt_bot_internal *bip) : bot(bip) {}
|
||||
};
|
||||
|
||||
#ifdef BRLCAD_OPENVDB
|
||||
|
||||
static bool
|
||||
bot_remesh_vdb(struct ged *UNUSED(gedp), struct rt_bot_internal *bot, double voxelSize)
|
||||
bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal **obot,
|
||||
struct rt_bot_internal *bot,
|
||||
double voxel_size, double adaptivity)
|
||||
{
|
||||
const float exteriorBandWidth = 10.0;
|
||||
const float interiorBandWidth = std::numeric_limits<float>::max();
|
||||
|
||||
struct botDataAdapter bda(bot);
|
||||
|
||||
openvdb::initialize();
|
||||
|
||||
bu_log("...voxelizing");
|
||||
|
||||
openvdb::math::Transform::Ptr xform = openvdb::math::Transform::createLinearTransform(voxelSize);
|
||||
openvdb::FloatGrid::Ptr bot2vol = openvdb::tools::meshToVolume<openvdb::FloatGrid, botDataAdapter>(bda, *xform, exteriorBandWidth, interiorBandWidth);
|
||||
|
||||
#if 0
|
||||
openvdb::io::File file("mesh.vdb");
|
||||
openvdb::GridPtrVec grids;
|
||||
grids.push_back(bot2vol);
|
||||
file.write(grids);
|
||||
file.close();
|
||||
return false;
|
||||
#endif
|
||||
|
||||
bu_log("...devoxelizing");
|
||||
|
||||
std::vector<openvdb::Vec3s> points;
|
||||
std::vector<openvdb::Vec3I> triangles;
|
||||
std::vector<openvdb::Vec4I> quadrilaterals;
|
||||
openvdb::tools::volumeToMesh<openvdb::FloatGrid>(*bot2vol, points, triangles, quadrilaterals);
|
||||
|
||||
bu_log("...storing");
|
||||
|
||||
if (bot->vertices) {
|
||||
bu_free(bot->vertices, "vertices");
|
||||
bot->num_vertices = 0;
|
||||
}
|
||||
if (bot->faces) {
|
||||
bu_free(bot->faces, "faces");
|
||||
bot->num_faces = 0;
|
||||
}
|
||||
if (bot->normals) {
|
||||
bu_free(bot->normals, "normals");
|
||||
}
|
||||
if (bot->face_normals) {
|
||||
bu_free(bot->face_normals, "face normals");
|
||||
if (bot->mode != RT_BOT_SOLID) {
|
||||
bu_vls_printf(gedp->ged_result_str,
|
||||
"ERROR: OpenVDB remesh requires a SOLID BoT.\n"
|
||||
"Surface and plate-mode BoTs have no volume; "
|
||||
"use Geogram remesh instead.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bot->num_vertices = points.size();
|
||||
bot->vertices = (fastf_t *)bu_malloc(bot->num_vertices * ELEMENTS_PER_POINT * sizeof(fastf_t), "vertices");
|
||||
for (size_t i = 0; i < points.size(); i++) {
|
||||
bot->vertices[(i*3)+X] = points[i].x();
|
||||
bot->vertices[(i*3)+Y] = points[i].y();
|
||||
bot->vertices[(i*3)+Z] = points[i].z();
|
||||
}
|
||||
bot->num_faces = triangles.size() + (quadrilaterals.size() * 2);
|
||||
bot->faces = (int *)bu_malloc(bot->num_faces * 3 * sizeof(int), "triangles");
|
||||
for (size_t i = 0; i < triangles.size(); i++) {
|
||||
bot->faces[(i*3)+X] = triangles[i].x();
|
||||
bot->faces[(i*3)+Y] = triangles[i].y();
|
||||
bot->faces[(i*3)+Z] = triangles[i].z();
|
||||
}
|
||||
size_t ntri = triangles.size();
|
||||
for (size_t i = 0; i < quadrilaterals.size(); i++) {
|
||||
bot->faces[((ntri+i)*3)+X] = quadrilaterals[i][0];
|
||||
bot->faces[((ntri+i)*3)+Y] = quadrilaterals[i][1];
|
||||
bot->faces[((ntri+i)*3)+Z] = quadrilaterals[i][2];
|
||||
bu_log("...voxelizing (voxel_size=%.4g, adaptivity=%.2f)\n",
|
||||
voxel_size > 0.0 ? voxel_size : -1.0, adaptivity);
|
||||
|
||||
bot->faces[((ntri+i+1)*3)+X] = quadrilaterals[i][0];
|
||||
bot->faces[((ntri+i+1)*3)+Y] = quadrilaterals[i][2];
|
||||
bot->faces[((ntri+i+1)*3)+Z] = quadrilaterals[i][3];
|
||||
openvdb::FloatGrid::Ptr grid = rt_bot_to_openvdb_sdf(bot, voxel_size);
|
||||
if (!grid) {
|
||||
bu_vls_printf(gedp->ged_result_str, "ERROR: meshToVolume failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bu_log("...done!\n");
|
||||
bu_log("...extracting mesh\n");
|
||||
|
||||
return (points.size() > 0);
|
||||
struct rt_bot_internal *nbot = rt_bot_from_openvdb_sdf(grid, adaptivity);
|
||||
if (!nbot || nbot->num_faces == 0) {
|
||||
bu_vls_printf(gedp->ged_result_str, "ERROR: volumeToMesh produced no faces\n");
|
||||
if (nbot) { bu_free(nbot->vertices, "verts"); bu_free(nbot->faces, "faces"); BU_PUT(nbot, struct rt_bot_internal); }
|
||||
return false;
|
||||
}
|
||||
|
||||
bu_log("...done! %zu vertices, %zu faces\n",
|
||||
nbot->num_vertices, nbot->num_faces);
|
||||
|
||||
*obot = nbot;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else /* OPENVDB_ABI_VERSION_NUMBER */
|
||||
#else /* BRLCAD_OPENVDB */
|
||||
|
||||
static bool
|
||||
bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal *UNUSED(bot), double UNUSED(voxelSize))
|
||||
bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal **UNUSED(obot),
|
||||
struct rt_bot_internal *UNUSED(bot),
|
||||
double UNUSED(voxel_size), double UNUSED(adaptivity))
|
||||
{
|
||||
bu_vls_printf(gedp->ged_result_str,
|
||||
"WARNING: BoT remeshing is unavailable.\n"
|
||||
@@ -166,7 +101,7 @@ bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal *UNUSED(bot), double UNU
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* OPENVDB_ABI_VERSION_NUMBER */
|
||||
#endif /* BRLCAD_OPENVDB */
|
||||
|
||||
static int
|
||||
bot_remesh_geogram(struct rt_bot_internal **obot, struct ged *gedp, struct rt_bot_internal *bot)
|
||||
@@ -240,7 +175,7 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
|
||||
struct _ged_bot_info *gb = (struct _ged_bot_info *)bs;
|
||||
struct ged *gedp = gb->gedp;
|
||||
|
||||
const char *usage_string = "bot [options] remesh <objname> <output_bot>\n";
|
||||
const char *usage_string = "bot [options] remesh <objname> [output_bot]\n";
|
||||
const char *purpose_string = "Store a remeshed version of the BoT in object <output_bot>";
|
||||
if (_bot_cmd_msgs(bs, argc, argv, usage_string, purpose_string)) {
|
||||
return BRLCAD_OK;
|
||||
@@ -257,14 +192,17 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
|
||||
|
||||
int print_help = 0;
|
||||
int use_vdb = 0;
|
||||
fastf_t vdb_voxel_size = 0.0; /* 0 = auto */
|
||||
fastf_t vdb_adaptivity = 0.0; /* 0 = full voxel density */
|
||||
struct bu_vls output_bot_name = BU_VLS_INIT_ZERO;
|
||||
|
||||
struct bu_opt_desc d[7];
|
||||
BU_OPT(d[0], "h", "help", "", NULL, &print_help, "Print help");
|
||||
BU_OPT(d[1], "o", "output", "oname", &bu_opt_vls, &output_bot_name, "Name to use for output BoT");
|
||||
BU_OPT(d[2], "", "vdb", "", NULL, &use_vdb, "Use OpenVDB based remeshing");
|
||||
BU_OPT_NULL(d[3]);
|
||||
|
||||
BU_OPT(d[0], "h", "help", "", NULL, &print_help, "Print help");
|
||||
BU_OPT(d[1], "o", "output", "oname", &bu_opt_vls, &output_bot_name, "Name to use for output BoT");
|
||||
BU_OPT(d[2], "", "vdb", "", NULL, &use_vdb, "Use OpenVDB level-set remeshing (solid BoTs only)");
|
||||
BU_OPT(d[3], "", "openvdb-voxel-size", "#", &bu_opt_fastf_t, &vdb_voxel_size, "OpenVDB voxel size in model units (default: bbox_diagonal/100)");
|
||||
BU_OPT(d[4], "", "openvdb-adaptivity", "#", &bu_opt_fastf_t, &vdb_adaptivity, "OpenVDB mesh simplification [0.0=full density, 1.0=max simplification]");
|
||||
BU_OPT_NULL(d[5]);
|
||||
|
||||
argc--; argv++;
|
||||
|
||||
@@ -283,15 +221,13 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
|
||||
}
|
||||
|
||||
const char *input_bot_name = gb->dp->d_namep;
|
||||
if (gb->intern->idb_major_type != DB5_MAJORTYPE_BRLCAD || gb->intern->idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
|
||||
if (gb->intern->idb_major_type != DB5_MAJORTYPE_BRLCAD ||
|
||||
gb->intern->idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
|
||||
bu_vls_printf(gedp->ged_result_str, "%s is not a BOT primitive\n", input_bot_name);
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
struct directory *dp_input = RT_DIR_NULL;
|
||||
struct directory *dp_output = RT_DIR_NULL;
|
||||
|
||||
GED_CHECK_READ_ONLY(gedp, BRLCAD_ERROR);
|
||||
|
||||
if ((bu_vls_strlen(&output_bot_name) && argc > 1) || argc > 2) {
|
||||
@@ -303,11 +239,10 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
|
||||
if (!bu_vls_strlen(&output_bot_name) && argc == 2)
|
||||
bu_vls_printf(&output_bot_name, "%s", argv[1]);
|
||||
|
||||
// If we've got no specified output, we're overwriting
|
||||
/* If no output name, overwrite the original. */
|
||||
if (!bu_vls_strlen(&output_bot_name))
|
||||
bu_vls_printf(&output_bot_name, "%s", input_bot_name);
|
||||
|
||||
|
||||
if (!BU_STR_EQUAL(input_bot_name, bu_vls_cstr(&output_bot_name))) {
|
||||
GED_CHECK_EXISTS(gedp, bu_vls_cstr(&output_bot_name), LOOKUP_QUIET, BRLCAD_ERROR);
|
||||
}
|
||||
@@ -315,61 +250,66 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
|
||||
struct rt_bot_internal *input_bot = (struct rt_bot_internal *)gb->intern->idb_ptr;
|
||||
RT_BOT_CK_MAGIC(input_bot);
|
||||
|
||||
bu_log("INPUT BoT has %zu vertices and %zu faces\n", input_bot->num_vertices, input_bot->num_faces);
|
||||
bu_log("INPUT BoT has %zu vertices and %zu faces\n",
|
||||
input_bot->num_vertices, input_bot->num_faces);
|
||||
|
||||
/* TODO: stash a backup if overwriting the original */
|
||||
struct rt_bot_internal *obot = NULL;
|
||||
|
||||
if (use_vdb) {
|
||||
bool ok = bot_remesh_vdb(gedp, input_bot, 50);
|
||||
if (!ok) {
|
||||
if (!bot_remesh_vdb(gedp, &obot, input_bot, vdb_voxel_size, vdb_adaptivity)) {
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
} else {
|
||||
struct rt_bot_internal *obot = NULL;
|
||||
if (bot_remesh_geogram(&obot, gedp, input_bot) != BRLCAD_OK || !obot) {
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
|
||||
intern.idb_type = ID_BOT;
|
||||
intern.idb_meth = &OBJ[ID_BOT];
|
||||
intern.idb_ptr = (void *)obot;
|
||||
|
||||
const char *rname = bu_vls_cstr(&output_bot_name);
|
||||
struct directory *dp = db_diradd(gedp->dbip, rname, RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, (void *)&intern.idb_type);
|
||||
if (dp == RT_DIR_NULL) {
|
||||
bu_vls_printf(gedp->ged_result_str, "Failed to write out new BoT %s\n", rname);
|
||||
rt_db_free_internal(&intern);
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
if (rt_db_put_internal(dp, gedp->dbip, &intern) < 0) {
|
||||
bu_vls_printf(gedp->ged_result_str, "Failed to write out new BoT %s\n", rname);
|
||||
rt_db_free_internal(&intern);
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
rt_db_free_internal(&intern);
|
||||
bu_vls_printf(gedp->ged_result_str, "Remesh complete\n");
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
bu_log("OUTPUT BoT has %zu vertices and %zu faces\n", input_bot->num_vertices, input_bot->num_faces);
|
||||
if (!obot) {
|
||||
bu_vls_printf(gedp->ged_result_str, "ERROR: remesh produced no output\n");
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
if (BU_STR_EQUAL(input_bot_name, bu_vls_cstr(&output_bot_name))) {
|
||||
dp_output = dp_input;
|
||||
bu_log("OUTPUT BoT has %zu vertices and %zu faces\n",
|
||||
obot->num_vertices, obot->num_faces);
|
||||
|
||||
/* Write out the result. */
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
|
||||
intern.idb_type = ID_BOT;
|
||||
intern.idb_meth = &OBJ[ID_BOT];
|
||||
intern.idb_ptr = (void *)obot;
|
||||
|
||||
const char *rname = bu_vls_cstr(&output_bot_name);
|
||||
struct directory *dp;
|
||||
|
||||
if (BU_STR_EQUAL(input_bot_name, rname)) {
|
||||
/* Overwrite in place. */
|
||||
dp = gb->dp;
|
||||
} else {
|
||||
GED_DB_DIRADD(gedp, dp_output, bu_vls_cstr(&output_bot_name), RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, (void *)&gb->intern->idb_type, BRLCAD_ERROR);
|
||||
dp = db_diradd(gedp->dbip, rname, RT_DIR_PHONY_ADDR, 0,
|
||||
RT_DIR_SOLID, (void *)&intern.idb_type);
|
||||
if (dp == RT_DIR_NULL) {
|
||||
bu_vls_printf(gedp->ged_result_str, "Failed to create output BoT %s\n", rname);
|
||||
rt_db_free_internal(&intern);
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
GED_DB_PUT_INTERN(gedp, dp_output, gb->intern, BRLCAD_ERROR);
|
||||
if (rt_db_put_internal(dp, gedp->dbip, &intern) < 0) {
|
||||
bu_vls_printf(gedp->ged_result_str, "Failed to write output BoT %s\n", rname);
|
||||
rt_db_free_internal(&intern);
|
||||
bu_vls_free(&output_bot_name);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
rt_db_free_internal(&intern);
|
||||
bu_vls_printf(gedp->ged_result_str, "Remesh complete\n");
|
||||
bu_vls_free(&output_bot_name);
|
||||
|
||||
return BRLCAD_OK;
|
||||
@@ -384,4 +324,3 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
|
||||
// c-file-style: "stroustrup"
|
||||
// End:
|
||||
// ex: shiftwidth=4 tabstop=8
|
||||
|
||||
|
||||
@@ -263,6 +263,8 @@ dp_tessellate(struct rt_bot_internal **obot, struct bu_vls *method_flag, struct
|
||||
settings.strict = 0;
|
||||
bu_vls_sprintf(method_flag, "REPAIR");
|
||||
ret = rt_bot_repair(obot, bot, &settings);
|
||||
if (ret == BRLCAD_OK && settings.used_openvdb)
|
||||
bu_vls_sprintf(method_flag, "REPAIR_OPENVDB");
|
||||
} else {
|
||||
// Already a valid BoT - tessellate is a no-op.
|
||||
*obot = NULL;
|
||||
|
||||
@@ -701,7 +701,7 @@ facetize_collect_primitive_summary(struct _ged_facetize_state *s)
|
||||
for (m_it = method_sets.begin(); m_it != method_sets.end(); ++m_it) {
|
||||
total += m_it->second.size();
|
||||
if (m_it->first == std::string("FAIL")) fail_cnt += m_it->second.size();
|
||||
if (m_it->first == std::string("REPAIR")) repair_cnt += m_it->second.size();
|
||||
if (m_it->first == std::string("REPAIR") || m_it->first == std::string("REPAIR_OPENVDB")) repair_cnt += m_it->second.size();
|
||||
if (m_it->first == std::string("PLATE")) plate_cnt += m_it->second.size();
|
||||
}
|
||||
bu_vls_printf(s->primitive_summary, " %-43s %8zu\n", "Total solids evaluated", total);
|
||||
@@ -712,6 +712,8 @@ facetize_collect_primitive_summary(struct _ged_facetize_state *s)
|
||||
for (m_it = method_sets.begin(); m_it != method_sets.end(); ++m_it) {
|
||||
if (m_it->first == std::string("REPAIR")) {
|
||||
bu_vls_printf(s->primitive_summary, " %-41s %8zu\n", "bot repair", m_it->second.size());
|
||||
} else if (m_it->first == std::string("REPAIR_OPENVDB")) {
|
||||
bu_vls_printf(s->primitive_summary, " %-41s %8zu\n", "OpenVDB level-set repair", m_it->second.size());
|
||||
} else if (m_it->first == std::string("PLATE")) {
|
||||
bu_vls_printf(s->primitive_summary, " %-41s %8zu\n", "plate extrusion", m_it->second.size());
|
||||
} else if (m_it->first == std::string("FAIL")) {
|
||||
|
||||
@@ -6,6 +6,21 @@ brlcad_find_package(OPENNURBS REQUIRED SYSPATTERN openNURBS)
|
||||
brlcad_find_package(REGEX REQUIRED)
|
||||
find_package_gte(REQUIRED)
|
||||
|
||||
set(LIBRT_OPENVDB_INCLUDE_DIRS)
|
||||
set(LIBRT_OPENVDB_LIBS)
|
||||
if(BRLCAD_ENABLE_OPENVDB)
|
||||
set(LIBRT_OPENVDB_INCLUDE_DIRS ${OpenVDB_INCLUDE_DIRS} ${Tbb_INCLUDE_DIRS} ${IlmBase_INCLUDE_DIRS})
|
||||
set(LIBRT_OPENVDB_LIBS ${OpenVDB_LIBRARIES} ${IlmBase_LIBRARIES} ${Tbb_LIBRARIES})
|
||||
# Prevent warnings in OpenVDB/Boost headers and an LTO false positive
|
||||
# involving OpenVDB's UINT64_MAX LeafManager sentinel.
|
||||
set_source_files_properties(
|
||||
primitives/bot/bot_openvdb.cpp
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS BRLCAD_OPENVDB
|
||||
COMPILE_FLAGS "-w -fno-lto"
|
||||
)
|
||||
endif(BRLCAD_ENABLE_OPENVDB)
|
||||
|
||||
if(BRLCAD_ENABLE_OPENCL)
|
||||
add_definitions(-DUSE_OPENCL)
|
||||
endif(BRLCAD_ENABLE_OPENCL)
|
||||
@@ -164,6 +179,7 @@ set(
|
||||
primitives/bot/bot_edge.c
|
||||
primitives/bot/bot_mirror.c
|
||||
primitives/bot/bot_oriented_bbox.cpp
|
||||
primitives/bot/bot_openvdb.cpp
|
||||
primitives/bot/bot_plot.cpp
|
||||
primitives/bot/btg.c
|
||||
primitives/bot/btgf.c
|
||||
@@ -435,6 +451,7 @@ set(
|
||||
${RPCRT_LIB}
|
||||
${STDCXX_LIBRARIES}
|
||||
manifold::manifold
|
||||
${LIBRT_OPENVDB_LIBS}
|
||||
)
|
||||
|
||||
# Include directories needed by librt users
|
||||
@@ -448,6 +465,7 @@ set(
|
||||
${BRLCAD_SOURCE_DIR}/src/libbu
|
||||
${BRLCAD_SOURCE_DIR}/src/libbg
|
||||
${REGEX_INCLUDE_DIRS}
|
||||
${LIBRT_OPENVDB_INCLUDE_DIRS}
|
||||
)
|
||||
# FIXME: needs materialX
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/../../../MaterialXSource/source
|
||||
@@ -567,6 +585,7 @@ cmakefiles(
|
||||
bot2nurbs.txt
|
||||
brep_csg_boolean.txt
|
||||
kigumi_todo.txt
|
||||
primitives/bot/bot_openvdb.h
|
||||
vds/README
|
||||
vds/COPYING
|
||||
vds/vds.h
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* B O T _ O P E N V D B . C P P
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2024-2025 United States Government as represented by
|
||||
* Copyright (c) 2024-2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
@@ -17,21 +17,24 @@
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @file libged/bot/bot_openvdb.cpp
|
||||
/** @file librt/primitives/bot/bot_openvdb.cpp
|
||||
*
|
||||
* Shared OpenVDB utilities for the bot plugin.
|
||||
* Shared OpenVDB utilities for BoT processing.
|
||||
*
|
||||
* Three building blocks are implemented here:
|
||||
* Four building blocks are implemented here:
|
||||
*
|
||||
* bot_to_sdf() — BoT → OpenVDB signed-distance field
|
||||
* sdf_to_bot() — OpenVDB SDF → rt_bot_internal
|
||||
* rt_rtip_to_occupancy_grid() — raytrace a prepped rt_i into a BoolGrid
|
||||
* rt_bot_to_openvdb_sdf() — BoT → OpenVDB signed-distance field
|
||||
* rt_bot_from_openvdb_sdf() — OpenVDB SDF → rt_bot_internal
|
||||
* rt_bot_openvdb_repair() — OpenVDB level-set BoT repair
|
||||
* rt_rtip_to_openvdb_occupancy() — raytrace a prepped rt_i into a BoolGrid
|
||||
*
|
||||
* See bot_openvdb.h for the public API documentation.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bot_openvdb.h"
|
||||
|
||||
#ifdef BRLCAD_OPENVDB
|
||||
|
||||
# include <cmath>
|
||||
@@ -53,8 +56,11 @@
|
||||
# include "rt/geom.h"
|
||||
# include "raytrace.h"
|
||||
|
||||
# include "bot_openvdb.h"
|
||||
|
||||
int
|
||||
rt_bot_openvdb_available(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* botDataAdapter
|
||||
@@ -84,10 +90,10 @@ struct botDataAdapter {
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* bot_to_sdf
|
||||
* rt_bot_to_openvdb_sdf
|
||||
* ---------------------------------------------------------------------- */
|
||||
openvdb::FloatGrid::Ptr
|
||||
bot_to_sdf(struct rt_bot_internal *bot, double voxel_size)
|
||||
rt_bot_to_openvdb_sdf(struct rt_bot_internal *bot, double voxel_size)
|
||||
{
|
||||
if (!bot || !bot->num_vertices || !bot->num_faces)
|
||||
return openvdb::FloatGrid::Ptr();
|
||||
@@ -128,7 +134,7 @@ bot_to_sdf(struct rt_bot_internal *bot, double voxel_size)
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* sdf_to_bot
|
||||
* rt_bot_from_openvdb_sdf
|
||||
*
|
||||
* Uses the class-based VolumeToMesh (rather than the volumeToMesh free
|
||||
* function) so that relaxDisorientedTriangles=true is in effect. That
|
||||
@@ -138,7 +144,7 @@ bot_to_sdf(struct rt_bot_internal *bot, double voxel_size)
|
||||
* the same isovalue/adaptivity settings.
|
||||
* ---------------------------------------------------------------------- */
|
||||
struct rt_bot_internal *
|
||||
sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity)
|
||||
rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity)
|
||||
{
|
||||
if (!grid)
|
||||
return NULL;
|
||||
@@ -183,7 +189,7 @@ sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity)
|
||||
|
||||
obot->num_vertices = n_points;
|
||||
obot->vertices = (fastf_t *)bu_malloc(n_points * 3 * sizeof(fastf_t),
|
||||
"sdf_to_bot vertices");
|
||||
"rt_bot_from_openvdb_sdf vertices");
|
||||
for (size_t i = 0; i < n_points; i++) {
|
||||
obot->vertices[i * 3 + X] = points[i].x();
|
||||
obot->vertices[i * 3 + Y] = points[i].y();
|
||||
@@ -191,7 +197,7 @@ sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity)
|
||||
}
|
||||
|
||||
obot->num_faces = n_tris;
|
||||
obot->faces = (int *)bu_malloc(n_tris * 3 * sizeof(int), "sdf_to_bot faces");
|
||||
obot->faces = (int *)bu_malloc(n_tris * 3 * sizeof(int), "rt_bot_from_openvdb_sdf faces");
|
||||
|
||||
/* Walk each polygon pool, emitting triangles then splitting quads. */
|
||||
size_t fidx = 0;
|
||||
@@ -223,9 +229,19 @@ sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity)
|
||||
return obot;
|
||||
}
|
||||
|
||||
static void
|
||||
free_openvdb_bot(struct rt_bot_internal *bot)
|
||||
{
|
||||
if (!bot)
|
||||
return;
|
||||
|
||||
rt_bot_internal_free(bot);
|
||||
BU_PUT(bot, struct rt_bot_internal);
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* bot_openvdb_repair
|
||||
* rt_bot_openvdb_repair
|
||||
*
|
||||
* Attempt to repair a non-manifold solid BoT using the OpenVDB level-set
|
||||
* pipeline. Converts the mesh to a signed-distance field and extracts a
|
||||
@@ -238,19 +254,22 @@ sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity)
|
||||
* Caller is responsible for freeing the returned bot.
|
||||
* ---------------------------------------------------------------------- */
|
||||
struct rt_bot_internal *
|
||||
bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size)
|
||||
rt_bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size, fastf_t *volume)
|
||||
{
|
||||
openvdb::FloatGrid::Ptr grid = bot_to_sdf(bot, voxel_size);
|
||||
if (!bot || bot->mode != RT_BOT_SOLID)
|
||||
return NULL;
|
||||
|
||||
openvdb::FloatGrid::Ptr grid = rt_bot_to_openvdb_sdf(bot, voxel_size);
|
||||
if (!grid) {
|
||||
bu_log("bot_openvdb_repair: meshToVolume failed\n");
|
||||
bu_log("rt_bot_openvdb_repair: meshToVolume failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Full voxel resolution (adaptivity=0) preserves shape best for repair. */
|
||||
struct rt_bot_internal *cand = sdf_to_bot(grid, 0.0);
|
||||
struct rt_bot_internal *cand = rt_bot_from_openvdb_sdf(grid, 0.0);
|
||||
if (!cand || cand->num_faces == 0) {
|
||||
bu_log("bot_openvdb_repair: volumeToMesh produced no faces\n");
|
||||
if (cand) { bu_free(cand->vertices, "verts"); bu_free(cand->faces, "faces"); BU_PUT(cand, struct rt_bot_internal); }
|
||||
bu_log("rt_bot_openvdb_repair: volumeToMesh produced no faces\n");
|
||||
free_openvdb_bot(cand);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -271,9 +290,8 @@ bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size)
|
||||
}
|
||||
manifold::Manifold mcheck(vcheck);
|
||||
if (mcheck.Status() != manifold::Manifold::Error::NoError) {
|
||||
bu_log("bot_openvdb_repair: result is not manifold (unexpected)\n");
|
||||
bu_free(cand->vertices, "verts"); bu_free(cand->faces, "faces");
|
||||
BU_PUT(cand, struct rt_bot_internal);
|
||||
bu_log("rt_bot_openvdb_repair: result is not manifold (unexpected)\n");
|
||||
free_openvdb_bot(cand);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -283,24 +301,34 @@ bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size)
|
||||
* and 2 of every triangle reverses the winding order globally and
|
||||
* turns it into a valid outward-facing solid without touching the
|
||||
* vertex data or rebuilding the mesh. */
|
||||
if (mcheck.Volume() <= 0.0) {
|
||||
bu_log("bot_openvdb_repair: result is inside-out; flipping winding order\n");
|
||||
fastf_t repair_volume = mcheck.Volume();
|
||||
if (repair_volume == 0.0) {
|
||||
bu_log("rt_bot_openvdb_repair: result has zero volume\n");
|
||||
free_openvdb_bot(cand);
|
||||
return NULL;
|
||||
}
|
||||
if (repair_volume < 0.0) {
|
||||
bu_log("rt_bot_openvdb_repair: result is inside-out; flipping winding order\n");
|
||||
for (size_t i = 0; i < cand->num_faces; i++) {
|
||||
int tmp = cand->faces[i * 3 + 1];
|
||||
cand->faces[i * 3 + 1] = cand->faces[i * 3 + 2];
|
||||
cand->faces[i * 3 + 2] = tmp;
|
||||
}
|
||||
repair_volume = -repair_volume;
|
||||
}
|
||||
|
||||
if (volume)
|
||||
*volume = repair_volume;
|
||||
|
||||
return cand;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* rt_rtip_to_occupancy_grid internals
|
||||
* rt_rtip_to_openvdb_occupancy internals
|
||||
*
|
||||
* These hit/miss callbacks and the per-ray context are used only by
|
||||
* rt_rtip_to_occupancy_grid below.
|
||||
* rt_rtip_to_openvdb_occupancy below.
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
struct OccRayCtx {
|
||||
@@ -357,11 +385,11 @@ occ_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* rt_rtip_to_occupancy_grid
|
||||
* rt_rtip_to_openvdb_occupancy
|
||||
* ---------------------------------------------------------------------- */
|
||||
openvdb::BoolGrid::Ptr
|
||||
rt_rtip_to_occupancy_grid(struct rt_i *rtip, double voxel_size,
|
||||
int *nx, int *ny, int *nz)
|
||||
rt_rtip_to_openvdb_occupancy(struct rt_i *rtip, double voxel_size,
|
||||
int *nx, int *ny, int *nz)
|
||||
{
|
||||
int nx_m = (int)std::ceil((rtip->mdl_max[X] - rtip->mdl_min[X]) / voxel_size);
|
||||
int ny_m = (int)std::ceil((rtip->mdl_max[Y] - rtip->mdl_min[Y]) / voxel_size);
|
||||
@@ -450,6 +478,20 @@ rt_rtip_to_occupancy_grid(struct rt_i *rtip, double voxel_size,
|
||||
return solid;
|
||||
}
|
||||
|
||||
#else /* BRLCAD_OPENVDB */
|
||||
|
||||
int
|
||||
rt_bot_openvdb_available(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rt_bot_internal *
|
||||
rt_bot_openvdb_repair(struct rt_bot_internal *UNUSED(bot), double UNUSED(voxel_size), fastf_t *UNUSED(volume))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* BRLCAD_OPENVDB */
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* B O T _ O P E N V D B . H
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2024-2025 United States Government as represented by
|
||||
* Copyright (c) 2024-2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
@@ -17,31 +17,49 @@
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @file libged/bot/bot_openvdb.h
|
||||
/** @file librt/primitives/bot/bot_openvdb.h
|
||||
*
|
||||
* Shared OpenVDB utilities for the bot plugin.
|
||||
* Shared OpenVDB utilities for BoT processing.
|
||||
*
|
||||
* Provides three building blocks used by remesh, repair, and the
|
||||
* Provides four building blocks used by remesh, repair, and the
|
||||
* flood-fill exterior classifier:
|
||||
*
|
||||
* bot_to_sdf() — BoT triangle mesh → OpenVDB FloatGrid SDF
|
||||
* sdf_to_bot() — FloatGrid SDF → solid rt_bot_internal
|
||||
* rt_rtip_to_occupancy_grid()— raytrace a prepped rt_i into a BoolGrid
|
||||
* rt_bot_to_openvdb_sdf() — BoT triangle mesh → FloatGrid SDF
|
||||
* rt_bot_from_openvdb_sdf() — FloatGrid SDF → solid BoT
|
||||
* rt_bot_openvdb_repair() — OpenVDB level-set BoT repair
|
||||
* rt_rtip_to_openvdb_occupancy() — raytrace an rt_i into a BoolGrid
|
||||
*
|
||||
* All declarations are guarded by OPENVDB_ABI_VERSION_NUMBER so that
|
||||
* the header is a no-op when compiled without OpenVDB. Every translation
|
||||
* unit that includes this header is expected to be compiled with the
|
||||
* OpenVDB ABI defines (i.e., BRLCAD_ENABLE_OPENVDB is ON).
|
||||
* OpenVDB-specific declarations are guarded by BRLCAD_OPENVDB. Translation
|
||||
* units using those declarations must be compiled with the OpenVDB headers
|
||||
* and ABI definitions.
|
||||
*/
|
||||
|
||||
#ifndef LIBGED_BOT_OPENVDB_H
|
||||
#define LIBGED_BOT_OPENVDB_H
|
||||
#ifndef LIBRT_BOT_OPENVDB_H
|
||||
#define LIBRT_BOT_OPENVDB_H
|
||||
|
||||
#include "common.h"
|
||||
#include "rt/defines.h"
|
||||
#include "rt/geom.h"
|
||||
|
||||
RT_EXPORT int rt_bot_openvdb_available(void);
|
||||
|
||||
/**
|
||||
* Repair a non-manifold solid BoT using the OpenVDB level-set pipeline.
|
||||
*
|
||||
* @param bot Source BoT (read-only).
|
||||
* @param voxel_size Voxel edge length in model units. Pass <= 0 to
|
||||
* auto-size to bbox_diagonal / 100.
|
||||
* @param volume Optional output for the absolute volume of the result.
|
||||
* @return New rt_bot_internal on success, NULL on failure.
|
||||
* Caller owns the result and must free it via
|
||||
* rt_bot_internal_free() / BU_PUT().
|
||||
*/
|
||||
RT_EXPORT struct rt_bot_internal *rt_bot_openvdb_repair(struct rt_bot_internal *bot,
|
||||
double voxel_size,
|
||||
fastf_t *volume);
|
||||
|
||||
#ifdef BRLCAD_OPENVDB
|
||||
|
||||
# include "rt/geom.h"
|
||||
# include "raytrace.h"
|
||||
|
||||
/* Suppress warnings from OpenVDB and its transitive headers. */
|
||||
@@ -72,7 +90,7 @@
|
||||
* auto-size to bbox_diagonal / 100.
|
||||
* @return FloatGrid SDF, or an empty Ptr on failure.
|
||||
*/
|
||||
openvdb::FloatGrid::Ptr bot_to_sdf(struct rt_bot_internal *bot, double voxel_size);
|
||||
RT_EXPORT openvdb::FloatGrid::Ptr rt_bot_to_openvdb_sdf(struct rt_bot_internal *bot, double voxel_size);
|
||||
|
||||
/**
|
||||
* Extract a solid BoT from an OpenVDB signed-distance field.
|
||||
@@ -80,31 +98,14 @@ openvdb::FloatGrid::Ptr bot_to_sdf(struct rt_bot_internal *bot, double voxel_siz
|
||||
* The output is always a closed 2-manifold (RT_BOT_SOLID, RT_BOT_CCW).
|
||||
* Quads produced by volumeToMesh are split into two triangles each.
|
||||
*
|
||||
* @param grid Source SDF (e.g. from bot_to_sdf or meshToVolume).
|
||||
* @param grid Source SDF (e.g. from rt_bot_to_openvdb_sdf).
|
||||
* @param adaptivity Mesh simplification factor [0.0, 1.0].
|
||||
* 0.0 = full voxel resolution; 1.0 = maximum merging.
|
||||
* @return New rt_bot_internal on success, NULL on failure.
|
||||
* Caller owns the result and must free it via
|
||||
* rt_bot_internal_free() / BU_PUT().
|
||||
*/
|
||||
struct rt_bot_internal *sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity);
|
||||
|
||||
/**
|
||||
* Repair a non-manifold solid BoT using the OpenVDB level-set pipeline.
|
||||
*
|
||||
* Converts the mesh to a signed-distance field and extracts a
|
||||
* guaranteed-manifold mesh from the 0-isovalue surface. The result is
|
||||
* validated for manifoldness and positive volume (negative volume would
|
||||
* indicate an inside-out reconstruction).
|
||||
*
|
||||
* @param bot Source BoT (read-only).
|
||||
* @param voxel_size Voxel edge length in model units. Pass <= 0 to
|
||||
* auto-size to bbox_diagonal / 100.
|
||||
* @return New rt_bot_internal on success, NULL on failure.
|
||||
* Caller owns the result and must free it via
|
||||
* rt_bot_internal_free() / BU_PUT().
|
||||
*/
|
||||
struct rt_bot_internal *bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size);
|
||||
RT_EXPORT struct rt_bot_internal *rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity);
|
||||
|
||||
/**
|
||||
* Voxelize a prepped rt_i into a solid-occupancy BoolGrid.
|
||||
@@ -119,12 +120,12 @@ struct rt_bot_internal *bot_openvdb_repair(struct rt_bot_internal *bot, double v
|
||||
* @param nx,ny,nz Output: grid dimensions including padding.
|
||||
* @return BoolGrid with active voxels = solid interior cells.
|
||||
*/
|
||||
openvdb::BoolGrid::Ptr rt_rtip_to_occupancy_grid(struct rt_i *rtip,
|
||||
double voxel_size,
|
||||
int *nx, int *ny, int *nz);
|
||||
RT_EXPORT openvdb::BoolGrid::Ptr rt_rtip_to_openvdb_occupancy(struct rt_i *rtip,
|
||||
double voxel_size,
|
||||
int *nx, int *ny, int *nz);
|
||||
|
||||
#endif /* BRLCAD_OPENVDB */
|
||||
#endif /* LIBGED_BOT_OPENVDB_H */
|
||||
#endif /* LIBRT_BOT_OPENVDB_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
@@ -1473,13 +1473,15 @@ rt_edit_bot_repair(struct bu_vls *log_str, struct rt_db_internal *ip, const stru
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct bu_opt_desc d[5];
|
||||
struct bu_opt_desc d[7];
|
||||
int options_json = 0;
|
||||
BU_OPT(d[0], "h", "help", "", NULL, &print_help, "Print help");
|
||||
BU_OPT(d[1], "p", "max-hole-percent", "#", bu_opt_fastf_t, &settings.max_hole_area_percent, "Maximum hole area to repair (percentage of mesh surface area)");
|
||||
BU_OPT(d[2], "a", "max-hole-area", " #", bu_opt_fastf_t, &settings.max_hole_area, "Maximum hole area to repair in mm (overrides -p option)");
|
||||
BU_OPT(d[3], "", "options-json", "", NULL, &options_json, "Return JSON of supported options");
|
||||
BU_OPT_NULL(d[4]);
|
||||
BU_OPT(d[3], "", "openvdb", "", NULL, &settings.force_openvdb, "Force OpenVDB level-set repair");
|
||||
BU_OPT(d[4], "", "openvdb-voxel-size", "#", bu_opt_fastf_t, &settings.openvdb_voxel_size, "OpenVDB voxel size in model units (default: bbox diagonal/100)");
|
||||
BU_OPT(d[5], "", "options-json", "", NULL, &options_json, "Return JSON of supported options");
|
||||
BU_OPT_NULL(d[6]);
|
||||
|
||||
if (argc > 0 && argv) {
|
||||
bu_opt_parse(NULL, argc, argv, d);
|
||||
@@ -1489,7 +1491,9 @@ rt_edit_bot_repair(struct bu_vls *log_str, struct rt_db_internal *ip, const stru
|
||||
if (log_str) {
|
||||
bu_vls_printf(log_str, "{\"options\":[");
|
||||
bu_vls_printf(log_str, "{\"name\":\"max-hole-percent\",\"type\":\"float\",\"description\":\"Maximum hole area to repair (percentage of mesh surface area)\"},");
|
||||
bu_vls_printf(log_str, "{\"name\":\"max-hole-area\",\"type\":\"float\",\"description\":\"Maximum hole area to repair in mm (overrides max-hole-percent)\"}");
|
||||
bu_vls_printf(log_str, "{\"name\":\"max-hole-area\",\"type\":\"float\",\"description\":\"Maximum hole area to repair in mm (overrides max-hole-percent)\"},");
|
||||
bu_vls_printf(log_str, "{\"name\":\"openvdb\",\"type\":\"boolean\",\"description\":\"Force OpenVDB level-set repair\"},");
|
||||
bu_vls_printf(log_str, "{\"name\":\"openvdb-voxel-size\",\"type\":\"float\",\"description\":\"OpenVDB voxel size in model units\"}");
|
||||
bu_vls_printf(log_str, "]}");
|
||||
}
|
||||
return 1;
|
||||
@@ -1512,7 +1516,8 @@ rt_edit_bot_repair(struct bu_vls *log_str, struct rt_db_internal *ip, const stru
|
||||
int rep_ret = rt_bot_repair(&obot, bot, &settings);
|
||||
|
||||
if (rep_ret < 0) {
|
||||
if (log_str) bu_vls_printf(log_str, "{\"status\":\"error\",\"message\":\"Unable to repair BoT\"}");
|
||||
if (log_str && settings.force_openvdb && !settings.openvdb_available) bu_vls_printf(log_str, "{\"status\":\"error\",\"message\":\"OpenVDB repair is unavailable in this build\"}");
|
||||
else if (log_str) bu_vls_printf(log_str, "{\"status\":\"error\",\"message\":\"Unable to repair BoT\"}");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1525,7 +1530,9 @@ rt_edit_bot_repair(struct bu_vls *log_str, struct rt_db_internal *ip, const stru
|
||||
ip->idb_meth->ft_ifree(ip);
|
||||
ip->idb_ptr = (void *)obot;
|
||||
|
||||
if (log_str) {
|
||||
if (log_str && settings.used_openvdb) {
|
||||
bu_vls_printf(log_str, "{\"status\":\"success\",\"message\":\"Successfully repaired BoT using OpenVDB\"}");
|
||||
} else if (log_str) {
|
||||
if (settings.output_data_loss) {
|
||||
bu_vls_printf(log_str, "{\"status\":\"success\",\"message\":\"Successfully repaired BoT; ");
|
||||
if (settings.output_data_loss & RT_BOT_REPAIR_LOST_NORMALS)
|
||||
|
||||
@@ -495,6 +495,31 @@ manifold_to_preserved_bot(const manifold::MeshGL64 &mesh,
|
||||
mesh.NumTri());
|
||||
}
|
||||
|
||||
static int
|
||||
openvdb_repair(struct rt_bot_internal **obot, struct rt_bot_internal *bot,
|
||||
struct rt_bot_repair_info *rinfo)
|
||||
{
|
||||
if (!rinfo->openvdb_available || bot->mode != RT_BOT_SOLID)
|
||||
return -1;
|
||||
|
||||
struct rt_bot_internal *repaired_bot = rt_bot_openvdb_repair(
|
||||
bot, rinfo->openvdb_voxel_size, &rinfo->output_volume);
|
||||
if (!repaired_bot)
|
||||
return -1;
|
||||
|
||||
rinfo->output_nonmanifold = 0;
|
||||
rinfo->used_openvdb = 1;
|
||||
if (rinfo->strict)
|
||||
rinfo->output_lint_fail = bot_repair_lint(repaired_bot);
|
||||
if (rinfo->output_lint_fail) {
|
||||
rt_bot_internal_free(repaired_bot);
|
||||
BU_PUT(repaired_bot, struct rt_bot_internal);
|
||||
return -1;
|
||||
}
|
||||
*obot = repaired_bot;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rt_bot_repair(struct rt_bot_internal **obot, struct rt_bot_internal *bot, struct rt_bot_repair_info *rinfo)
|
||||
{
|
||||
@@ -508,6 +533,11 @@ rt_bot_repair(struct rt_bot_internal **obot, struct rt_bot_internal *bot, struct
|
||||
rinfo->output_lint_fail = 0;
|
||||
rinfo->output_volume = 0.0;
|
||||
rinfo->output_data_loss = 0;
|
||||
rinfo->openvdb_available = rt_bot_openvdb_available();
|
||||
rinfo->used_openvdb = 0;
|
||||
|
||||
if (rinfo->force_openvdb)
|
||||
return openvdb_repair(obot, bot, rinfo);
|
||||
if (bot->mode != RT_BOT_SOLID || !bot->num_vertices ||
|
||||
!bot->num_faces || bot->num_vertices > INT_MAX ||
|
||||
bot->num_faces > INT_MAX || !bot->vertices || !bot->faces)
|
||||
@@ -598,9 +628,10 @@ rt_bot_repair(struct rt_bot_internal **obot, struct rt_bot_internal *bot, struct
|
||||
if (repair_ret < 0 || !rfaces || !rpnts) {
|
||||
bu_free(rfaces, "rfaces");
|
||||
bu_free(rpnts, "rpnts");
|
||||
if (rinfo->openvdb_available)
|
||||
return openvdb_repair(obot, bot, rinfo);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validate the repaired mesh with Manifold.
|
||||
manifold::MeshGL64 gmm;
|
||||
for (int i = 0; i < n_rpnts; i++) {
|
||||
@@ -619,11 +650,16 @@ rt_bot_repair(struct rt_bot_internal **obot, struct rt_bot_internal *bot, struct
|
||||
manifold::Manifold gmanifold(gmm);
|
||||
if (gmanifold.Status() != manifold::Manifold::Error::NoError) {
|
||||
rinfo->output_nonmanifold = 1;
|
||||
if (rinfo->openvdb_available)
|
||||
return openvdb_repair(obot, bot, rinfo);
|
||||
return -1;
|
||||
}
|
||||
rinfo->output_volume = gmanifold.Volume();
|
||||
if (rinfo->output_volume < 0)
|
||||
if (rinfo->output_volume < 0) {
|
||||
if (rinfo->openvdb_available)
|
||||
return openvdb_repair(obot, bot, rinfo);
|
||||
return -1;
|
||||
}
|
||||
|
||||
manifold::MeshGL64 omesh = gmanifold.GetMeshGL64();
|
||||
struct rt_bot_internal *nbot = manifold_to_bot(omesh);
|
||||
|
||||
Reference in New Issue
Block a user