Compare commits

...
2 Commits
Author SHA1 Message Date
Clifford Yapp f21c45e755 Use bu_vls for VFONT paths
The VFONT loader used a 128-byte buffer when constructing the bundled
font path. Odd-pathname distcheck directories exceeded that limit in CI
environments, causing fblabel to reject the default font and all
dependent rtwizard tests to fail. Use a bu_vls path so the installed
font can be opened regardless of build prefix length, and release it on
all loader exits.
2026-09-18 19:54:20 +00:00
Clifford Yapp 52c83ac3a1 Implement some OpenVDB based repair functionality and flood filling 2026-09-18 19:32:32 +00:00
13 changed files with 310 additions and 250 deletions
+8 -1
View File
@@ -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
View File
@@ -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;
}
+5 -5
View File
@@ -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
+2 -2
View File
@@ -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. */
+1 -1
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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;
+3 -1
View File
@@ -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")) {
+19
View File
@@ -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:
+13 -6
View File
@@ -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)
+38 -2
View File
@@ -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);