star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

commit e3a68b1ef3c4629f805d5ee3062169f44e34ea06
parent 4bb93ba6016e7a58372347e0bd7686b6aeb858f6
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 27 Jul 2015 16:54:08 +0200

Add and test the s3d_shape_compute_volume function

This function computes the volume of a shape. If the shape is an
instantiated scene, the returned value is the sum of the volumes of the
instantiated shapes. Note that this function assumes that the shape
defines a 2D manifold closed volume and that the face normals point into
the volume.

Diffstat:
Msrc/s3d.h | 7+++++++
Msrc/s3d_instance.c | 24++++++++++++++++++++++++
Msrc/s3d_instance.h | 4++++
Msrc/s3d_mesh.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_mesh.h | 5+++++
Msrc/s3d_shape.c | 17+++++++++++++++++
Msrc/test_s3d_shape.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 168 insertions(+), 0 deletions(-)

diff --git a/src/s3d.h b/src/s3d.h @@ -342,6 +342,13 @@ s3d_shape_compute_area (struct s3d_shape* shape, float* area); +/* This function assumes that the shape defines a closed volume and that the + * normals point into the volume */ +S3D_API res_T +s3d_shape_compute_volume + (struct s3d_shape* shape, + float* volume); + /******************************************************************************* * Sampler API ******************************************************************************/ diff --git a/src/s3d_instance.c b/src/s3d_instance.c @@ -132,6 +132,7 @@ instance_compute_area(struct instance* inst) struct list_node* node; float area = 0.f; ASSERT(inst); + /* TODO take into account the scale factor of the instance */ LIST_FOR_EACH(node, &inst->scene->shapes) { struct s3d_shape* shape = CONTAINER_OF (node, struct s3d_shape, scene_attachment); @@ -148,3 +149,26 @@ instance_compute_area(struct instance* inst) return area; } +float +instance_compute_volume(struct instance* inst) +{ + struct list_node* node; + float volume = 0.f; + ASSERT(inst); + /* TODO take into account the scale factor of the instance */ + LIST_FOR_EACH(node, &inst->scene->shapes) { + struct s3d_shape* shape = CONTAINER_OF + (node, struct s3d_shape, scene_attachment); + switch(shape->type) { + case SHAPE_INSTANCE: + volume += instance_compute_volume(shape->data.instance); + break; + case SHAPE_MESH: + volume += mesh_compute_volume(shape->data.mesh, inst->flip_surface); + break; + default: FATAL("Unreachable code\n"); break; + } + } + return volume; +} + diff --git a/src/s3d_instance.h b/src/s3d_instance.h @@ -70,5 +70,9 @@ extern LOCAL_SYM float instance_compute_area (struct instance* inst); +extern LOCAL_SYM float +instance_compute_volume + (struct instance* inst); + #endif /* S3D_INSTANCE_H */ diff --git a/src/s3d_mesh.c b/src/s3d_mesh.c @@ -361,6 +361,53 @@ mesh_compute_area(struct mesh* mesh) return area; } +float +mesh_compute_volume(struct mesh* mesh, const char flip_surface) +{ + const uint32_t* ids; + const float* pos; + size_t itri, ntris; + double volume = 0.0; + char flip; + ASSERT(mesh); + + ntris = mesh_get_ntris(mesh); + if(!ntris) + return 0.f; + + ids = mesh_get_ids(mesh); + pos = mesh_get_pos(mesh); + flip = mesh->flip_surface ^ flip_surface; + + /* Build a tetrahedron whose face is the mesh triangle and its appex is the + * origin. Then compute the volume of the tetrahedron and add or sub it from + * the overall volume whether the normal point toward or backward the appex */ + FOR_EACH(itri, 0, ntris) { + float E0[3], E1[3], N[3]; + float B, h; + const size_t id = itri * 3/*#ids per faces*/; + const float* v0 = pos + ids[id+0]*3/*#coords*/; + const float* v1 = pos + ids[id+1]*3/*#coords*/; + const float* v2 = pos + ids[id+2]*3/*#coords*/; + /* Front face is CW by default */ + f3_sub(E0, v2, v0); + f3_sub(E1, v1, v0); + if(flip) { + f3_cross(N, E1, E0); + } else { + f3_cross(N, E0, E1); + } + B = f3_normalize(N, N) * 0.5f; /* Base area */ + h = -f3_dot(N, v0); /* Height from the base to the appex */ + volume += (h*B); + } + + if(volume < 0.0 && volume > 3.e-6) + volume = 0; + ASSERT(volume >= 0.0); + return (float)(volume / 3.0); +} + res_T mesh_setup_indexed_vertices (struct mesh* mesh, diff --git a/src/s3d_mesh.h b/src/s3d_mesh.h @@ -116,6 +116,11 @@ extern LOCAL_SYM float mesh_compute_area (struct mesh* mesh); +extern LOCAL_SYM float +mesh_compute_volume + (struct mesh* mesh, + const char flip_surface); /* Flip the shape surface or not */ + extern LOCAL_SYM res_T mesh_setup_indexed_vertices (struct mesh* mesh, diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -259,6 +259,23 @@ s3d_shape_compute_area(struct s3d_shape* shape, float* area) } res_T +s3d_shape_compute_volume(struct s3d_shape* shape, float* volume) +{ + if(!shape || !volume) + return RES_BAD_ARG; + switch(shape->type) { + case SHAPE_MESH: + *volume = mesh_compute_volume(shape->data.mesh, 0); + break; + case SHAPE_INSTANCE: + *volume = instance_compute_volume(shape->data.instance); + break; + default: FATAL("Unreachable code \n"); break; + } + return RES_OK; +} + +res_T s3d_instance_set_position (struct s3d_shape* shape, const float position[3]) { diff --git a/src/test_s3d_shape.c b/src/test_s3d_shape.c @@ -36,6 +36,54 @@ #include <rsys/math.h> +static const float cube_verts[] = { + 5.f, 5.f, 5.f, + 6.f, 5.f, 5.f, + 5.f, 6.f, 5.f, + 6.f, 6.f, 5.f, + 5.f, 5.f, 6.f, + 6.f, 5.f, 6.f, + 5.f, 6.f, 6.f, + 6.f, 6.f, 6.f +}; +static const unsigned cube_nverts= sizeof(cube_verts) / sizeof(float[3]); + +/* Front faces are CW. The normals point into the cube */ +static const unsigned cube_ids[] = { + 0, 2, 1, 1, 2, 3, /* Front */ + 0, 4, 2, 2, 4, 6, /* Left */ + 4, 5, 6, 6, 5, 7, /* Back */ + 3, 7, 1, 1, 7, 5, /* Right */ + 2, 6, 3, 3, 6, 7, /* Top */ + 0, 1, 4, 4, 1, 5 /* Bottom */ +}; +static const unsigned cube_ntris = sizeof(cube_ids) / sizeof(unsigned[3]); + +static void +cube_get_ids(const unsigned itri, unsigned ids[3], void* data) +{ + const unsigned id = itri * 3; + CHECK(data, NULL); + NCHECK(ids, NULL); + CHECK(itri < cube_ntris, 1); + ids[0] = cube_ids[id + 0]; + ids[1] = cube_ids[id + 1]; + ids[2] = cube_ids[id + 2]; +} + +static void +cube_get_pos(const unsigned ivert, float pos[3], void* data) +{ + const unsigned i = ivert*3; + (void)data; + CHECK(data, NULL); + NCHECK(pos, NULL); + CHECK(ivert < cube_nverts, 1); + pos[0] = cube_verts[i + 0]; + pos[1] = cube_verts[i + 1]; + pos[2] = cube_verts[i + 2]; +} + int main(int argc, char** argv) { @@ -48,6 +96,7 @@ main(int argc, char** argv) size_t nprims; float pos[3]; float area; + float volume; const unsigned cbox_ntris = sizeof(cbox_walls_ids) / sizeof(unsigned[3]); const unsigned cbox_nverts = sizeof(cbox_walls) / sizeof(float[3]); unsigned id; @@ -275,6 +324,21 @@ main(int argc, char** argv) CHECK(s3d_shape_ref_put(shape), RES_OK); CHECK(s3d_shape_ref_put(inst), RES_OK); CHECK(s3d_scene_ref_put(scn), RES_OK); + + attribs[0].type = S3D_FLOAT3; + attribs[0].usage = S3D_POSITION; + attribs[0].get = cube_get_pos; + attribs[1] = S3D_VERTEX_DATA_NULL; + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices + (shape, cube_ntris, cube_get_ids, cube_nverts, attribs, NULL), RES_OK); + CHECK(s3d_shape_primitives_count(shape, &nprims), RES_OK); + CHECK(nprims, 12); + CHECK(s3d_shape_compute_area(shape, &area), RES_OK); + CHECK(eq_epsf(area, 6.f, 1.e-6f), 1); + CHECK(s3d_shape_compute_volume(shape, &volume), RES_OK); + CHECK(eq_epsf(volume, 1.f, 1.e-6f), 1); + CHECK(s3d_shape_ref_put(shape), RES_OK); CHECK(s3d_device_ref_put(dev), RES_OK);; check_memory_allocator(&allocator);