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:
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);