Fix BoT shots with origins inside mesh bounds
Commit7376385fd2backed the HLBVH traversal origin by twice the soltab bounding radius to preserve entrance triangles. Commit1d75918492replaced that conservative distance with max(0, -r_min), assuming r_min reaches the solid boundary. During rt_shootray, however, r_min can describe the current spatial partition cell. A ray starting inside a BoT can enter that cell after entering the mesh. BVH culling then drops the entrance triangles, and segment assembly discards the exit hit without an entrance. In an overlapping scene, ARS and native BoT differ because the ARS RPP test refreshes r_min. For origins inside the BoT BVH root box, move the traversal origin just beyond the box's entry face. This retains the entrance facets without the excess traversal from a bounding radius backout. Rays originating outside the box need no backout. Share the calculation between scalar and vector shots, while keeping triangle intersections relative to the original ray origin. Five deterministic, single processor Generic_Twin.g renders per view had identical pixels with both backout methods. The box method cut shot time by 3.5% from inside the flight deck console, 24.5% from inside a main strut, and 5.9% from inside a tire. An exterior view differed by about 0.2% from the radius method. Avoiding unnecessary BVH traversal more than offsets the extra box backout arithmetic for these interior views. Test direct ARS and BoT shots whose origins are inside the mesh, including vector shots, and an overlapping composite scene that reproduces the failure through rt_shootray without setting r_min manually.
This commit is contained in:
1 parent
bcfb8669c1
commit
de09372b73
3 files changed
+383
-25
No files matched your search
@@ -727,8 +727,46 @@ bot_makesegs_specific(hit_da *hits,
|
||||
struct rt_piecestate *psp);
|
||||
|
||||
|
||||
/* r_min can describe a spatial partition cell that begins inside this
|
||||
* solid. Start BVH traversal just before the ray enters the BoT's own
|
||||
* root box so entrance triangles behind the ray origin remain visible. */
|
||||
static inline void
|
||||
bot_backed_origin(point_t b_pt, const struct xray *rp, const struct bvh_flat_node *root,
|
||||
const vect_t inv_dir, const struct soltab *stp)
|
||||
{
|
||||
fastf_t backout = MAX_FASTF;
|
||||
int axis;
|
||||
|
||||
for (axis = X; axis <= Z; axis++) {
|
||||
if (rp->r_pt[axis] < root->bounds[axis] ||
|
||||
rp->r_pt[axis] > root->bounds[axis + 3]) {
|
||||
VMOVE(b_pt, rp->r_pt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (axis = X; axis <= Z; axis++) {
|
||||
fastf_t distance;
|
||||
if (rp->r_dir[axis] > 0.0)
|
||||
distance = (rp->r_pt[axis] - root->bounds[axis]) * inv_dir[axis];
|
||||
else if (rp->r_dir[axis] < 0.0)
|
||||
distance = (root->bounds[axis + 3] - rp->r_pt[axis]) * -inv_dir[axis];
|
||||
else
|
||||
continue;
|
||||
backout = FMIN(backout, distance);
|
||||
}
|
||||
|
||||
if (backout < MAX_FASTF) {
|
||||
fastf_t padding = stp->st_rtip ? stp->st_rtip->rti_tol.dist : BN_TOL_DIST;
|
||||
VJOIN1(b_pt, rp->r_pt, -(backout + padding), rp->r_dir);
|
||||
} else {
|
||||
VMOVE(b_pt, rp->r_pt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bot_shot_hlbvh_flat(struct bvh_flat_node *root, struct xray* rp, triangle_s *tris, size_t ntris, hit_da* hits, fastf_t toldist)
|
||||
bot_shot_hlbvh_flat(struct bvh_flat_node *root, struct xray* rp, triangle_s *tris, size_t ntris, hit_da* hits, fastf_t toldist, const struct soltab *stp)
|
||||
{
|
||||
struct bvh_flat_node *stack_node[HLBVH_STACK_SIZE];
|
||||
unsigned char stack_child_index[HLBVH_STACK_SIZE];
|
||||
@@ -744,25 +782,8 @@ bot_shot_hlbvh_flat(struct bvh_flat_node *root, struct xray* rp, triangle_s *tri
|
||||
inverse_r_dir[1] = RAYDIR_INV(rp->r_dir[1]);
|
||||
inverse_r_dir[2] = RAYDIR_INV(rp->r_dir[2]);
|
||||
|
||||
// Because we are doing solid shotlining, we need to intersect all
|
||||
// triangles on the line of the ray, even when the r_pt is inside the mesh.
|
||||
// This means we DON'T want to cull hlbvh leaves behind our r_pt when the
|
||||
// r_pt is inside the mesh. To avoid this, we back our r_pt up to the
|
||||
// bounding sphere if needed. This incurs a slight
|
||||
// performance penalty when r_pt is past the mesh but still within the
|
||||
// bounding sphere diameter distance, since backing up will cause false
|
||||
// bbox intersect matches in those cases. This is necessary to ensure we
|
||||
// get the intersections needed for solid segment creation - the subsequent
|
||||
// full intersection solves will still produce the correct results.
|
||||
//
|
||||
// NOTE: We DO, however, need to use the real rp->r_pt when doing the
|
||||
// actual intersection solve math so our segments end up in the right
|
||||
// place.
|
||||
fastf_t backout = FMAX(0.0, -rp->r_min);
|
||||
point_t b_pt;
|
||||
b_pt[X] = rp->r_pt[X] - backout * rp->r_dir[X];
|
||||
b_pt[Y] = rp->r_pt[Y] - backout * rp->r_dir[Y];
|
||||
b_pt[Z] = rp->r_pt[Z] - backout * rp->r_dir[Z];
|
||||
bot_backed_origin(b_pt, rp, root, inverse_r_dir, stp);
|
||||
|
||||
while (stack_ind >= 0) {
|
||||
if (UNLIKELY(stack_ind >= HLBVH_STACK_SIZE)) {
|
||||
@@ -924,7 +945,7 @@ rt_bot_shot_specific(struct bot_specific *bot, struct soltab *stp, struct xray *
|
||||
toldist = (DBL_EPSILON * stp->st_aradius * 10);
|
||||
}
|
||||
|
||||
bot_shot_hlbvh_flat(sps->root, rp, sps->tris, bot->bot_ntri, &hits_per_cpu, toldist);
|
||||
bot_shot_hlbvh_flat(sps->root, rp, sps->tris, bot->bot_ntri, &hits_per_cpu, toldist, stp);
|
||||
|
||||
if (hits_per_cpu.count == 0) {
|
||||
return 0;
|
||||
@@ -1026,14 +1047,10 @@ bot_vshot_packet(struct soltab *stp, struct bot_specific *bot,
|
||||
|
||||
/* Per-ray setup: inverse dir, backed-up origin, reset hit list. */
|
||||
for (k = 0; k < m; k++) {
|
||||
fastf_t backout;
|
||||
inv_dir[k][0] = bot_vshot_rdinv(rp[k]->r_dir[0]);
|
||||
inv_dir[k][1] = bot_vshot_rdinv(rp[k]->r_dir[1]);
|
||||
inv_dir[k][2] = bot_vshot_rdinv(rp[k]->r_dir[2]);
|
||||
backout = FMAX(0.0, -rp[k]->r_min);
|
||||
b_pt[k][X] = rp[k]->r_pt[X] - backout * rp[k]->r_dir[X];
|
||||
b_pt[k][Y] = rp[k]->r_pt[Y] - backout * rp[k]->r_dir[Y];
|
||||
b_pt[k][Z] = rp[k]->r_pt[Z] - backout * rp[k]->r_dir[Z];
|
||||
bot_backed_origin(b_pt[k], rp[k], sps->root, inv_dir[k], stp);
|
||||
vhits[k].count = 0;
|
||||
segp[k].seg_stp = (struct soltab *)0;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ brlcad_addexec(rt_bot_split
|
||||
target_include_directories(rt_bot_split PRIVATE ${BRLCAD_SOURCE_DIR}/src/libbg)
|
||||
brlcad_add_test(NAME rt_bot_split COMMAND rt_bot_split)
|
||||
|
||||
brlcad_addexec(rt_ars_inside ars_inside.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
|
||||
brlcad_add_test(NAME rt_ars_inside COMMAND rt_ars_inside)
|
||||
|
||||
# boolweave testing
|
||||
brlcad_addexec(rt_boolweave rt_boolweave.c "${RT_TEST_LIBS}" TEST)
|
||||
brlcad_add_test(NAME rt_boolweave COMMAND rt_boolweave)
|
||||
@@ -202,6 +205,7 @@ brlcad_add_test(NAME rt_metaball_adjust COMMAND rt_metaball_adjust)
|
||||
set(
|
||||
distcheck_files
|
||||
CMakeLists.txt
|
||||
ars_inside.c
|
||||
arb_intersect.g
|
||||
binary_attribute.c
|
||||
binunif.c
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
/* A R S _ I N S I D E . C
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bu/app.h"
|
||||
#include "bu/log.h"
|
||||
#include "bu/malloc.h"
|
||||
#include "raytrace.h"
|
||||
#include "rt/geom.h"
|
||||
#include "wdb.h"
|
||||
|
||||
#define RING_SIDES 8
|
||||
#define RING_POINTS (RING_SIDES + 1)
|
||||
|
||||
static int
|
||||
check_shot(const struct rt_functab *ftp, struct soltab *stp, struct application *ap, struct xray *ray, const char *label)
|
||||
{
|
||||
struct seg seghead;
|
||||
struct seg *segp;
|
||||
struct resource *resp = ap->a_resource;
|
||||
int nhits;
|
||||
int failed = 0;
|
||||
|
||||
BU_LIST_INIT(&seghead.l);
|
||||
nhits = ftp->ft_shot(stp, ray, ap, &seghead);
|
||||
if (nhits <= 0 || BU_LIST_IS_EMPTY(&seghead.l)) {
|
||||
bu_log("%s: inside-origin ray missed\n", label);
|
||||
failed = 1;
|
||||
} else {
|
||||
segp = BU_LIST_FIRST(seg, &seghead.l);
|
||||
if (!NEAR_EQUAL(segp->seg_in.hit_dist, -9.0, SMALL_FASTF) ||
|
||||
!NEAR_EQUAL(segp->seg_out.hit_dist, 7.0, SMALL_FASTF)) {
|
||||
bu_log("%s: interval %g..%g, expected -9..7\n", label,
|
||||
segp->seg_in.hit_dist, segp->seg_out.hit_dist);
|
||||
failed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (BU_LIST_WHILE(segp, seg, &seghead.l)) {
|
||||
BU_LIST_DEQUEUE(&segp->l);
|
||||
RT_FREE_SEG(segp, resp);
|
||||
}
|
||||
|
||||
if (ftp->ft_vshot) {
|
||||
struct soltab *stps[1] = {stp};
|
||||
struct xray *rays[1] = {ray};
|
||||
struct seg vseg = {0};
|
||||
ftp->ft_vshot(stps, rays, &vseg, 1, ap);
|
||||
if (vseg.seg_stp != stp ||
|
||||
!NEAR_EQUAL(vseg.seg_in.hit_dist, -9.0, SMALL_FASTF) ||
|
||||
!NEAR_EQUAL(vseg.seg_out.hit_dist, 7.0, SMALL_FASTF)) {
|
||||
bu_log("%s: vector shot missed or returned wrong interval\n", label);
|
||||
failed = 1;
|
||||
}
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
|
||||
struct scene_result {
|
||||
int subject_hits;
|
||||
fastf_t out_dist;
|
||||
};
|
||||
|
||||
static int
|
||||
scene_hit(struct application *ap, struct partition *part_head,
|
||||
struct seg *UNUSED(finished_segs))
|
||||
{
|
||||
struct scene_result *result = (struct scene_result *)ap->a_uptr;
|
||||
struct partition *part;
|
||||
|
||||
for (part = part_head->pt_forw; part != part_head; part = part->pt_forw) {
|
||||
if (strstr(part->pt_regionp->reg_name, "subject.r")) {
|
||||
result->subject_hits++;
|
||||
result->out_dist = part->pt_outhit->hit_dist;
|
||||
}
|
||||
}
|
||||
return result->subject_hits > 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scene_miss(struct application *UNUSED(ap))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The support sphere makes the cut tree start the ray's first cell
|
||||
* inside the subject, while the subject itself spans the entire cell. */
|
||||
static int
|
||||
check_scene(int subject_id, const fastf_t *const curve_data[3],
|
||||
fastf_t *vertices, int *faces, const char *file)
|
||||
{
|
||||
struct db_i *dbip = NULL;
|
||||
struct rt_wdb *wdbp = NULL;
|
||||
struct rt_i *rtip = NULL;
|
||||
struct resource resp = RT_RESOURCE_INIT_ZERO;
|
||||
struct wmember members;
|
||||
struct application ap;
|
||||
struct scene_result result = {0};
|
||||
point_t support_center;
|
||||
int failed = 0;
|
||||
|
||||
if (file) {
|
||||
wdbp = wdb_fopen(file);
|
||||
if (wdbp)
|
||||
dbip = wdbp->dbip;
|
||||
} else {
|
||||
dbip = db_create_inmem();
|
||||
if (dbip)
|
||||
wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM);
|
||||
}
|
||||
if (!wdbp) {
|
||||
if (dbip)
|
||||
db_close(dbip);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (subject_id == ID_ARS) {
|
||||
fastf_t **curves = (fastf_t **)bu_malloc(3 * sizeof(fastf_t *), "ARS curves");
|
||||
size_t i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
curves[i] = (fastf_t *)bu_malloc(RING_POINTS * ELEMENTS_PER_POINT *
|
||||
sizeof(fastf_t), "ARS curve");
|
||||
memcpy(curves[i], curve_data[i],
|
||||
RING_POINTS * ELEMENTS_PER_POINT * sizeof(fastf_t));
|
||||
}
|
||||
failed += mk_ars(wdbp, "subject.s", 3, RING_POINTS, curves) != 0;
|
||||
} else {
|
||||
failed += mk_bot(wdbp, "subject.s", RT_BOT_SOLID, RT_BOT_CCW, 0,
|
||||
RING_SIDES + 2, RING_SIDES * 2, vertices, faces,
|
||||
NULL, NULL) != 0;
|
||||
}
|
||||
BU_LIST_INIT(&members.l);
|
||||
if (!mk_addmember("subject.s", &members.l, NULL, WMOP_UNION))
|
||||
failed++;
|
||||
failed += mk_lcomb(wdbp, "subject.r", &members, 1, NULL, NULL, NULL, 0) != 0;
|
||||
|
||||
VSET(support_center, 1.5, 0, 4);
|
||||
failed += mk_sph(wdbp, "support.s", support_center, 1.0) != 0;
|
||||
BU_LIST_INIT(&members.l);
|
||||
if (!mk_addmember("support.s", &members.l, NULL, WMOP_UNION))
|
||||
failed++;
|
||||
failed += mk_lcomb(wdbp, "support.r", &members, 1, NULL, NULL, NULL, 0) != 0;
|
||||
if (failed)
|
||||
goto done;
|
||||
|
||||
rtip = rt_i_create(dbip);
|
||||
if (!rtip || rt_gettree(rtip, "subject.r") ||
|
||||
rt_gettree(rtip, "support.r")) {
|
||||
failed = 1;
|
||||
goto done;
|
||||
}
|
||||
rt_prep(rtip);
|
||||
rt_init_resource(&resp, 0, rtip);
|
||||
|
||||
RT_APPLICATION_INIT(&ap);
|
||||
ap.a_rt_i = rtip;
|
||||
ap.a_resource = &resp;
|
||||
ap.a_hit = scene_hit;
|
||||
ap.a_miss = scene_miss;
|
||||
ap.a_uptr = &result;
|
||||
VSET(ap.a_ray.r_pt, 3, 0, 4);
|
||||
VSET(ap.a_ray.r_dir, 1, 0, 0);
|
||||
rt_shootray(&ap);
|
||||
if (result.subject_hits != 1 ||
|
||||
!NEAR_EQUAL(result.out_dist, 5.0, rtip->rti_tol.dist)) {
|
||||
bu_log("%s scene: %d subject partitions, exit %.17g; expected one exit at 5\n",
|
||||
subject_id == ID_ARS ? "ARS" : "BoT",
|
||||
result.subject_hits, result.out_dist);
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
done:
|
||||
if (rtip)
|
||||
rt_i_destroy(rtip);
|
||||
wdb_close(wdbp);
|
||||
return failed;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
fastf_t curve_data[3][RING_POINTS * ELEMENTS_PER_POINT] = {{0}};
|
||||
fastf_t *curves[3] = {curve_data[0], curve_data[1], curve_data[2]};
|
||||
fastf_t bot_vertices[(RING_SIDES + 2) * ELEMENTS_PER_POINT] = {0};
|
||||
int bot_faces[RING_SIDES * 2 * 3];
|
||||
struct rt_bot_internal bot = {0};
|
||||
struct rt_ars_internal ars = {0};
|
||||
struct rt_db_internal intern;
|
||||
struct rt_db_internal bot_intern;
|
||||
struct soltab st = {0};
|
||||
struct soltab bot_st = {0};
|
||||
struct rt_i *rtip;
|
||||
struct resource *resp = &rt_uniresource;
|
||||
struct application ap;
|
||||
struct xray ray = {0};
|
||||
vect_t invdir;
|
||||
size_t i;
|
||||
int failed = 0;
|
||||
|
||||
(void)argc;
|
||||
bu_setprogname(argv[0]);
|
||||
|
||||
for (i = 0; i < RING_POINTS; i++) {
|
||||
fastf_t angle = 2.0 * M_PI * (i % RING_SIDES) / RING_SIDES;
|
||||
VSET(&curve_data[1][i * ELEMENTS_PER_POINT],
|
||||
10.0 * cos(angle), 10.0 * sin(angle), 5.0);
|
||||
if (i < RING_SIDES)
|
||||
VMOVE(&bot_vertices[(i + 1) * ELEMENTS_PER_POINT],
|
||||
&curve_data[1][i * ELEMENTS_PER_POINT]);
|
||||
curve_data[2][i * ELEMENTS_PER_POINT + Z] = 10.0;
|
||||
}
|
||||
bot_vertices[(RING_SIDES + 1) * ELEMENTS_PER_POINT + Z] = 10.0;
|
||||
for (i = 0; i < RING_SIDES; i++) {
|
||||
int current = (int)i + 1;
|
||||
int next = (int)((i + 1) % RING_SIDES) + 1;
|
||||
int *bottom = &bot_faces[i * 6];
|
||||
int *top = &bottom[3];
|
||||
bottom[0] = 0;
|
||||
bottom[1] = next;
|
||||
bottom[2] = current;
|
||||
top[0] = current;
|
||||
top[1] = next;
|
||||
top[2] = RING_SIDES + 1;
|
||||
}
|
||||
bot.magic = RT_BOT_INTERNAL_MAGIC;
|
||||
bot.mode = RT_BOT_SOLID;
|
||||
bot.orientation = RT_BOT_CCW;
|
||||
bot.num_vertices = RING_SIDES + 2;
|
||||
bot.vertices = bot_vertices;
|
||||
bot.num_faces = RING_SIDES * 2;
|
||||
bot.faces = bot_faces;
|
||||
ars.magic = RT_ARS_INTERNAL_MAGIC;
|
||||
ars.ncurves = 3;
|
||||
ars.pts_per_curve = RING_POINTS;
|
||||
ars.curves = curves;
|
||||
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
|
||||
intern.idb_type = ID_ARS;
|
||||
intern.idb_meth = &OBJ[ID_ARS];
|
||||
intern.idb_ptr = &ars;
|
||||
RT_DB_INTERNAL_INIT(&bot_intern);
|
||||
bot_intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
|
||||
bot_intern.idb_type = ID_BOT;
|
||||
bot_intern.idb_meth = &OBJ[ID_BOT];
|
||||
bot_intern.idb_ptr = ⊥
|
||||
|
||||
rtip = rt_dirbuild_inmem(NULL, 0, NULL, 0);
|
||||
if (!rtip)
|
||||
return 1;
|
||||
rt_init_resource(resp, 0, rtip);
|
||||
|
||||
st.l.magic = RT_SOLTAB_MAGIC;
|
||||
st.l2.magic = RT_SOLTAB2_MAGIC;
|
||||
st.st_rtip = rtip;
|
||||
st.st_meth = &OBJ[ID_ARS];
|
||||
st.st_id = ID_ARS;
|
||||
if (OBJ[ID_ARS].ft_prep(&st, &intern, rtip)) {
|
||||
bu_log("ARS prep failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bot_st.l.magic = RT_SOLTAB_MAGIC;
|
||||
bot_st.l2.magic = RT_SOLTAB2_MAGIC;
|
||||
bot_st.st_rtip = rtip;
|
||||
bot_st.st_meth = &OBJ[ID_BOT];
|
||||
bot_st.st_id = ID_BOT;
|
||||
if (OBJ[ID_BOT].ft_prep(&bot_st, &bot_intern, rtip)) {
|
||||
bu_log("BoT prep failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
RT_APPLICATION_INIT(&ap);
|
||||
ap.a_rt_i = rtip;
|
||||
ap.a_resource = resp;
|
||||
|
||||
ray.magic = RT_RAY_MAGIC;
|
||||
VSET(ray.r_pt, 1, 0, 4);
|
||||
VSET(ray.r_dir, 1, 0, 0);
|
||||
ray.r_min = -0.05; /* the current spatial cell starts inside the ARS */
|
||||
failed += check_shot(&OBJ[ID_ARS], &st, &ap, &ray, "ARS cell bound");
|
||||
failed += check_shot(&OBJ[ID_BOT], &bot_st, &ap, &ray, "BoT cell bound");
|
||||
|
||||
VSET(invdir, 1, INFINITY, INFINITY);
|
||||
if (!rt_in_rpp(&ray, invdir, st.st_min, st.st_max)) {
|
||||
bu_log("ARS bounding box missed the test ray\n");
|
||||
failed++;
|
||||
} else {
|
||||
failed += check_shot(&OBJ[ID_ARS], &st, &ap, &ray, "ARS solid bound");
|
||||
}
|
||||
|
||||
ray.r_min = -0.05;
|
||||
if (!rt_in_rpp(&ray, invdir, bot_st.st_min, bot_st.st_max)) {
|
||||
bu_log("BoT bounding box missed the test ray\n");
|
||||
failed++;
|
||||
} else {
|
||||
failed += check_shot(&OBJ[ID_BOT], &bot_st, &ap, &ray, "BoT solid bound");
|
||||
}
|
||||
|
||||
OBJ[ID_ARS].ft_free(&st);
|
||||
OBJ[ID_BOT].ft_free(&bot_st);
|
||||
rt_i_destroy(rtip);
|
||||
|
||||
if (argc != 1 && argc != 3) {
|
||||
bu_log("Usage: %s [ars_scene.g bot_scene.g]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
{
|
||||
const fastf_t *scene_curves[3] = {curve_data[0], curve_data[1], curve_data[2]};
|
||||
failed += check_scene(ID_ARS, scene_curves, bot_vertices, bot_faces,
|
||||
argc == 3 ? argv[1] : NULL);
|
||||
failed += check_scene(ID_BOT, scene_curves, bot_vertices, bot_faces,
|
||||
argc == 3 ? argv[2] : NULL);
|
||||
}
|
||||
return failed ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user