Raw File
OBB_Volume.h
#pragma once

#include "SurfaceMeshModel.h"

// Special math library
#include "OBB_Volume_math.h"

// Eigen: for rotations
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace Eigen;

class OBB_Volume{

private:
	// computes the OBB for this set of points relative to this transform matrix.
	void computeOBB(size_t vcount,const REAL *points,size_t pstride,REAL *sides,REAL *matrix)
	{
		const char *src = (const char *) points;

		double max_dbl = DBL_MAX, min_dbl = -DBL_MAX;
		REAL bmin[3] = { max_dbl, max_dbl, max_dbl };
		REAL bmax[3] = { min_dbl, min_dbl, min_dbl };

		for (size_t i=0; i<vcount; i++)
		{
			const REAL *p = (const REAL *) src;
			REAL t[3];

			fm_inverseRT(matrix, p, t ); // inverse rotate translate

			if ( t[0] < bmin[0] ) bmin[0] = t[0];
			if ( t[1] < bmin[1] ) bmin[1] = t[1];
			if ( t[2] < bmin[2] ) bmin[2] = t[2];

			if ( t[0] > bmax[0] ) bmax[0] = t[0];
			if ( t[1] > bmax[1] ) bmax[1] = t[1];
			if ( t[2] > bmax[2] ) bmax[2] = t[2];

			src+=pstride;
		}

		REAL center[3];

		sides[0] = bmax[0]-bmin[0];
		sides[1] = bmax[1]-bmin[1];
		sides[2] = bmax[2]-bmin[2];

		center[0] = sides[0]*0.5f+bmin[0];
		center[1] = sides[1]*0.5f+bmin[1];
		center[2] = sides[2]*0.5f+bmin[2];

		REAL ocenter[3];

		fm_rotate(matrix,center,ocenter);

		matrix[12]+=ocenter[0];
		matrix[13]+=ocenter[1];
		matrix[14]+=ocenter[2];

	}

	void fm_computeBestFitOBB(size_t vcount,const REAL *points,size_t pstride,REAL *sides,REAL *matrix,bool bruteForce)
	{
		REAL plane[4];
		fm_computeBestFitPlane(vcount,points,pstride,0,0,plane);
		fm_planeToMatrix(plane,matrix);
		computeOBB( vcount, points, pstride, sides, matrix );

		REAL refmatrix[16];
		memcpy(refmatrix,matrix,16*sizeof(REAL));

		REAL volume = sides[0]*sides[1]*sides[2];
		if ( bruteForce )
		{
			for (REAL a=10; a<180; a+=10)
			{
				REAL quat[4];
				fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat);
				REAL temp[16];
				REAL pmatrix[16];
				fm_quatToMatrix(quat,temp);
				fm_matrixMultiply(temp,refmatrix,pmatrix);
				REAL psides[3];
				computeOBB( vcount, points, pstride, psides, pmatrix );
				REAL v = psides[0]*psides[1]*psides[2];
				if ( v < volume )
				{
					volume = v;
					memcpy(matrix,pmatrix,sizeof(REAL)*16);
					sides[0] = psides[0];
					sides[1] = psides[1];
					sides[2] = psides[2];
				}
			}
		}
	}

	void fm_computeBestFitOBB(size_t vcount,const REAL *points,size_t pstride,REAL *sides,REAL *pos,REAL *quat,bool bruteForce)
	{
		REAL matrix[16];
		fm_computeBestFitOBB(vcount,points,pstride,sides,matrix,bruteForce);
		fm_getTranslation(matrix,pos);
		fm_matrixToQuat(matrix,quat);
	}

public:

	Eigen::Matrix<double,3,1,Eigen::DontAlign>  sides;
	Eigen::Matrix<double,3,1,Eigen::DontAlign>  translation;
	Eigen::Matrix<double,4,1,Eigen::DontAlign>  rotation; // as quat.

	bool isReady;

	OBB_Volume(Surface_mesh * mesh = NULL)
	{
		if(mesh == NULL) 
		{
			isReady = false;
			return;
		}

		// Get points
		std::vector<Vector3d> pnts;

		Surface_mesh::Vertex_property<Point> points = mesh->vertex_property<Point>("v:point");
		Surface_mesh::Vertex_iterator vit, vend = mesh->vertices_end();

		for (vit = mesh->vertices_begin(); vit != vend; ++vit)
			pnts.push_back(points[vit]);

		sides = translation = Vector3d(0,0,0);
		rotation = Vector4d(0,0,0,0);

		fm_computeBestFitOBB(pnts.size(), &pnts.front()[0], sizeof(Vector3d), &sides[0], &translation[0], &rotation[0], true);

		isReady = true;
	}

	std::vector<Vector3d> corners()
	{
		Vector3d p(translation.x(), translation.y(), translation.z());
		Vector4d r(rotation[0], rotation[1], rotation[2], rotation[3]);

		Transform<double,3,Affine> t = Translation3d(p) * Quaterniond(r);

		double width = sides.x()/2;
		double length = sides.y()/2;
		double height = sides.z()/2;

		Vector3d  c[8];
		c[0] = Vector3d (width, length, height);
		c[1] = Vector3d (-width, length, height);
		c[2] = Vector3d (-width, -length, height);
		c[3] = Vector3d (width, -length, height);
		c[4] = Vector3d (width, length, -height);
		c[5] = Vector3d (-width, length, -height);
		c[6] = Vector3d (-width, -length, -height);
		c[7] = Vector3d (width, -length, -height);

		std::vector<Vector3d> result;

		for(int i = 0; i < 8; i++)
		{
			Vector3d p(c[i][0], c[i][1], c[i][2]);
			Vector3d pt = t * p;
			result.push_back( Vector3d(pt[0], pt[1], pt[2]) );
		}

		return result;
	}

	void draw()
	{
		if(!isReady) return;

		std::vector<Vector3d> corner = corners();
		Vector3d  c1, c2, c3, c4;
		Vector3d  bc1, bc2, bc3, bc4;
		c1 = corner[0];	c2 = corner[1];
		c3 = corner[2];	c4 = corner[3];
		bc1 = corner[4]; bc2 = corner[5];
		bc3 = corner[6]; bc4 = corner[7];

		glDisable(GL_LIGHTING);

		glColor3d(0, 0, 1);
		glLineWidth(1.0);

		glBegin(GL_LINES);
		glVertex3dv(c1.data());glVertex3dv(bc1.data());
		glVertex3dv(c2.data());glVertex3dv(bc2.data());
		glVertex3dv(c3.data());glVertex3dv(bc3.data());
		glVertex3dv(c4.data());glVertex3dv(bc4.data());
		glVertex3dv(c1.data());glVertex3dv(c2.data());
		glVertex3dv(c3.data());glVertex3dv(c4.data());
		glVertex3dv(c1.data());glVertex3dv(c4.data());
		glVertex3dv(c2.data());glVertex3dv(c3.data());
		glVertex3dv(bc1.data());glVertex3dv(bc2.data());
		glVertex3dv(bc3.data());glVertex3dv(bc4.data());
		glVertex3dv(bc1.data());glVertex3dv(bc4.data());
		glVertex3dv(bc2.data());glVertex3dv(bc3.data());
		glEnd();

		glEnable(GL_LIGHTING);

		glPopMatrix();
	}

	std::vector<Vector3d> axis()
	{
		std::vector<Vector3d> result;

		Vector3f p(0, 0, 0);
		Vector4f r(rotation[0], rotation[1], rotation[2], rotation[3]);

		Transform<float,3,Affine> t = Translation3f(p) * Quaternionf(r);

		Vector3f xAxis(1,0,0);	xAxis = t * xAxis;
		Vector3f yAxis(0,1,0);	yAxis = t * yAxis;
		Vector3f zAxis(0,0,1);	zAxis = t * zAxis;

		result.push_back(Vector3d(xAxis[0], xAxis[1], xAxis[2]));
		result.push_back(Vector3d(yAxis[0], yAxis[1], yAxis[2]));
		result.push_back(Vector3d(zAxis[0], zAxis[1], zAxis[2]));

		return result;
	}

	Point center()
	{
		return translation;
	}

	Vector3d extents()
	{
		return sides * 0.5;
	}
};

back to top