[Bf-blender-cvs] [d983908] soc-2013-paint: Fix T41019: Calculate Mass does not calculate actual volume.

Lukas Tönne noreply at git.blender.org
Sat Jul 12 12:44:58 CEST 2014


Commit: d983908f5a326f0a30a35d6acc2a804471cc88cd
Author: Lukas Tönne
Date:   Fri Jul 11 12:06:13 2014 +0200
https://developer.blender.org/rBd983908f5a326f0a30a35d6acc2a804471cc88cd

Fix T41019: Calculate Mass does not calculate actual volume.

This was a ToDo item, for mesh-based rigid body shapes (trimesh, convex)
the operator was simply using the bounding box volume, which can grossly
overestimate the volume and mass.

Calculating the actual volume of a mesh is not so difficult after all,
see e.g.
http://research.microsoft.com/en-us/um/people/chazhang/publications/icip01_ChaZhang.pdf

This patch also allows calculating the center-of-mass in the same way.
This is currently unused, because the rigid body system assumes the CoM
to be the same as the geometric object center. This is fine most of the
time, adding such user settings for "center-of-mass offset" would also
add quite a bit of complexity in user space, but it could be necessary
at some point. A number of other physical properties could be calculated
using the same principle, e.g. the moment of inertia.

===================================================================

M	source/blender/blenkernel/BKE_mesh.h
M	source/blender/blenkernel/BKE_rigidbody.h
M	source/blender/blenkernel/intern/mesh_evaluate.c
M	source/blender/blenkernel/intern/rigidbody.c
M	source/blender/blenlib/BLI_math_geom.h
M	source/blender/blenlib/intern/math_geom.c
M	source/blender/editors/physics/rigidbody_object.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 587dea5..975a8fd 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -212,6 +212,10 @@ bool BKE_mesh_center_median(struct Mesh *me, float cent[3]);
 bool BKE_mesh_center_bounds(struct Mesh *me, float cent[3]);
 bool BKE_mesh_center_centroid(struct Mesh *me, float cent[3]);
 
+void BKE_mesh_calc_volume(struct MVert *mverts, int numVerts,
+                          struct MFace *mfaces, int numFaces,
+                          float *r_vol, float *r_com);
+
 /* tessface */
 void BKE_mesh_loops_to_mface_corners(
         struct CustomData *fdata, struct CustomData *ldata,
diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h
index 86be3bf..c946f3a 100644
--- a/source/blender/blenkernel/BKE_rigidbody.h
+++ b/source/blender/blenkernel/BKE_rigidbody.h
@@ -69,6 +69,9 @@ void BKE_rigidbody_world_groups_relink(struct RigidBodyWorld *rbw);
 /* 'validate' (i.e. make new or replace old) Physics-Engine objects */
 void BKE_rigidbody_validate_sim_world(struct Scene *scene, struct RigidBodyWorld *rbw, bool rebuild);
 
+void BKE_rigidbody_calc_volume(struct Object *ob, float *r_vol);
+void BKE_rigidbody_calc_center_of_mass(struct Object *ob, float r_com[3]);
+
 /* -------------- */
 /* Utilities */
 
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index cb0386b..7e547ec 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1096,6 +1096,125 @@ bool BKE_mesh_center_centroid(Mesh *me, float cent[3])
 
 /* -------------------------------------------------------------------- */
 
+/** \name Mesh Volume Calculation
+ * \{ */
+
+static bool mesh_calc_center_centroid_ex(MVert *mverts, int UNUSED(numVerts),
+                                         MFace *mfaces, int numFaces,
+                                         float center[3])
+{
+	float totweight;
+	int f;
+	
+	zero_v3(center);
+	
+	if (numFaces == 0)
+		return false;
+	
+	totweight = 0.0f;
+	for (f = 0; f < numFaces; ++f) {
+		MFace *face = &mfaces[f];
+		MVert *v1 = &mverts[face->v1];
+		MVert *v2 = &mverts[face->v2];
+		MVert *v3 = &mverts[face->v3];
+		MVert *v4 = &mverts[face->v4];
+		float area;
+		
+		area = area_tri_v3(v1->co, v2->co, v3->co);
+		madd_v3_v3fl(center, v1->co, area);
+		madd_v3_v3fl(center, v2->co, area);
+		madd_v3_v3fl(center, v3->co, area);
+		totweight += area;
+		
+		if (face->v4) {
+			area = area_tri_v3(v3->co, v4->co, v1->co);
+			madd_v3_v3fl(center, v3->co, area);
+			madd_v3_v3fl(center, v4->co, area);
+			madd_v3_v3fl(center, v1->co, area);
+			totweight += area;
+		}
+	}
+	if (totweight == 0.0f)
+		return false;
+	
+	mul_v3_fl(center, 1.0f / (3.0f * totweight));
+	
+	return true;
+}
+
+void BKE_mesh_calc_volume(MVert *mverts, int numVerts,
+                          MFace *mfaces, int numFaces,
+                          float *r_vol, float *r_com)
+{
+	float center[3];
+	float totvol;
+	int f;
+	
+	if (r_vol) *r_vol = 0.0f;
+	if (r_com) zero_v3(r_com);
+	
+	if (numFaces == 0)
+		return;
+	
+	if (!mesh_calc_center_centroid_ex(mverts, numVerts, mfaces, numFaces, center))
+		return;
+	
+	totvol = 0.0f;
+	for (f = 0; f < numFaces; ++f) {
+		MFace *face = &mfaces[f];
+		MVert *v1 = &mverts[face->v1];
+		MVert *v2 = &mverts[face->v2];
+		MVert *v3 = &mverts[face->v3];
+		MVert *v4 = &mverts[face->v4];
+		float vol;
+		
+		vol = volume_tetrahedron_signed_v3(center, v1->co, v2->co, v3->co);
+		if (r_vol) {
+			totvol += vol;
+		}
+		if (r_com) {
+			/* averaging factor 1/4 is applied in the end */
+			madd_v3_v3fl(r_com, center, vol); // XXX could extract this
+			madd_v3_v3fl(r_com, v1->co, vol);
+			madd_v3_v3fl(r_com, v2->co, vol);
+			madd_v3_v3fl(r_com, v3->co, vol);
+		}
+		
+		if (face->v4) {
+			vol = volume_tetrahedron_signed_v3(center, v3->co, v4->co, v1->co);
+			
+			if (r_vol) {
+				totvol += vol;
+			}
+			if (r_com) {
+				/* averaging factor 1/4 is applied in the end */
+				madd_v3_v3fl(r_com, center, vol); // XXX could extract this
+				madd_v3_v3fl(r_com, v3->co, vol);
+				madd_v3_v3fl(r_com, v4->co, vol);
+				madd_v3_v3fl(r_com, v1->co, vol);
+			}
+		}
+	}
+	
+	/* Note: Depending on arbitrary centroid position,
+	 * totvol can become negative even for a valid mesh.
+	 * The true value is always the positive value.
+	 */
+	if (r_vol) {
+		*r_vol = fabsf(totvol);
+	}
+	if (r_com) {
+		/* Note: Factor 1/4 is applied once for all vertices here.
+		 * This also automatically negates the vector if totvol is negative.
+		 */
+		if (totvol != 0.0f)
+			mul_v3_fl(r_com, 0.25f / totvol);
+	}
+}
+
+
+/* -------------------------------------------------------------------- */
+
 /** \name NGon Tessellation (NGon/Tessface Conversion)
  * \{ */
 
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 0929350..2aaf8ad 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -57,6 +57,7 @@
 #include "BKE_effect.h"
 #include "BKE_global.h"
 #include "BKE_library.h"
+#include "BKE_mesh.h"
 #include "BKE_object.h"
 #include "BKE_pointcache.h"
 #include "BKE_rigidbody.h"
@@ -455,6 +456,182 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild)
 
 /* --------------------- */
 
+/* helper function to calculate volume of rigidbody object */
+// TODO: allow a parameter to specify method used to calculate this?
+void BKE_rigidbody_calc_volume(Object *ob, float *r_vol)
+{
+	RigidBodyOb *rbo = ob->rigidbody_object;
+
+	float size[3]  = {1.0f, 1.0f, 1.0f};
+	float radius = 1.0f;
+	float height = 1.0f;
+
+	float volume = 0.0f;
+
+	/* if automatically determining dimensions, use the Object's boundbox
+	 *	- assume that all quadrics are standing upright on local z-axis
+	 *	- assume even distribution of mass around the Object's pivot
+	 *	  (i.e. Object pivot is centralised in boundbox)
+	 *	- boundbox gives full width
+	 */
+	// XXX: all dimensions are auto-determined now... later can add stored settings for this
+	BKE_object_dimensions_get(ob, size);
+
+	if (ELEM3(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) {
+		/* take radius as largest x/y dimension, and height as z-dimension */
+		radius = MAX2(size[0], size[1]) * 0.5f;
+		height = size[2];
+	}
+	else if (rbo->shape == RB_SHAPE_SPHERE) {
+		/* take radius to the the largest dimension to try and encompass everything */
+		radius = max_fff(size[0], size[1], size[2]) * 0.5f;
+	}
+
+	/* calculate volume as appropriate  */
+	switch (rbo->shape) {
+		case RB_SHAPE_BOX:
+			volume = size[0] * size[1] * size[2];
+			break;
+
+		case RB_SHAPE_SPHERE:
+			volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius;
+			break;
+
+		/* for now, assume that capsule is close enough to a cylinder... */
+		case RB_SHAPE_CAPSULE:
+		case RB_SHAPE_CYLINDER:
+			volume = (float)M_PI * radius * radius * height;
+			break;
+
+		case RB_SHAPE_CONE:
+			volume = (float)M_PI / 3.0f * radius * radius * height;
+			break;
+
+		case RB_SHAPE_CONVEXH:
+		case RB_SHAPE_TRIMESH:
+		{
+			if (ob->type == OB_MESH) {
+				DerivedMesh *dm = rigidbody_get_mesh(ob);
+				MVert *mvert;
+				MFace *mface;
+				int totvert, totface;
+				
+				/* ensure mesh validity, then grab data */
+				if (dm == NULL)
+					return;
+			
+				DM_ensure_tessface(dm);
+			
+				mvert   = (dm) ? dm->getVertArray(dm) : NULL;
+				totvert = (dm) ? dm->getNumVerts(dm) : 0;
+				mface   = (dm) ? dm->getTessFaceArray(dm) : NULL;
+				totface = (dm) ? dm->getNumTessFaces(dm) : 0;
+				
+				if (totvert > 0 && totface > 0) {
+					BKE_mesh_calc_volume(mvert, totvert, mface, totface, &volume, NULL);
+				}
+				
+				/* cleanup temp data */
+				if (dm && ob->rigidbody_object->mesh_source == RBO_MESH_BASE) {
+					dm->release(dm);
+				}
+			}
+			else {
+				/* rough estimate from boundbox as fallback */
+				/* XXX could implement other types of geometry here (curves, etc.) */
+				volume = size[0] * size[1] * size[2];
+			}
+			break;
+		}
+
+#if 0 // XXX: not defined yet
+		case RB_SHAPE_COMPOUND:
+			volume = 0.0f;
+			break;
+#endif
+	}
+
+	/* return the volume calculated */
+	if (r_vol) *r_vol = volume;
+}
+
+void BKE_rigidbody_calc_center_of_mass(Object *ob, float r_com[3])
+{
+	RigidBodyOb *rbo = ob->rigidbody_object;
+
+	float size[3]  = {1.0f, 1.0f, 1.0f};
+	float height = 1.0f;
+
+	zero_v3(r_com);
+
+	/* if automatically determining dimensions, use the Object's boundbox
+	 *	- assume that all quadrics are standing upright on local z-axis
+	 *	- assume even distribution of mass around the Object's pivot
+	 *	  (i.e. Object pivot is centralised in boundbox)
+	 *	- boundbox gives full width
+	 */
+	// XXX: all dimensions are auto-determined now... later can add stored settings for this
+	BKE_object_dimensions_get(ob, size);
+
+	/* calculate volume as appropriate  */
+	switch (rbo->shape) {
+		case RB_SHAPE_BOX:
+		case RB_SHAPE_SPHERE:
+		case RB_SHAPE_CAPSULE:
+		case RB_SHAPE_CYLINDER:
+			break;
+
+		case RB_SHAPE_CONE:
+			/* take radius as largest x/y dimension, and height as z-dimension */
+			height = size[2];
+			/* cone is geometrically centered on the median,
+			 * center of mass is 1/4 up from the base
+			 */
+			r_com[2] = -0.25f * height;
+			break;
+
+		case RB_SHAPE_CONVEXH:
+		case RB_SHAPE_TRIMESH:
+		{
+			if (ob->type == OB_MESH) {
+				DerivedMesh *dm = rigidbody_get_mesh(ob);
+				MVert *mvert;
+				MFace *mface;
+				int totvert, totface;
+				
+				/* ensure mesh validity, then grab data */
+				if (dm == NULL)
+					return;
+			
+				DM_ensure_tessface(dm);
+			
+				mvert   = (dm) ? dm->getVertArray(dm) : NULL;
+				totvert = (dm) ? dm->getNumVerts(dm) : 0;
+				mface   = (dm) ? dm->getTessFaceArray(dm) : NULL;
+				totface = (dm) ? dm->getNumTessFaces(dm) : 0;
+				
+				if (totvert > 0 && totface > 0) {
+					BKE_mesh_calc_volume(mvert, totvert, mface, totface, NULL, r_com);
+				}
+				
+				/* cleanup temp data */
+				if (dm && ob->rigidbody_object->mesh_source == RBO_MESH_BASE) {
+					dm->release(d

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list