Example #1
0
void VoronoiFractureDemo::voronoiConvexHullShatter(const btAlignedObjectArray<btVector3>& points, const btAlignedObjectArray<btVector3>& verts, const btQuaternion& bbq, const btVector3& bbt, btScalar matDensity) {
	// points define voronoi cells in world space (avoid duplicates)
	// verts = source (convex hull) mesh vertices in local space
	// bbq & bbt = source (convex hull) mesh quaternion rotation and translation
	// matDensity = Material density for voronoi shard mass calculation
	btConvexHullComputer* convexHC = new btConvexHullComputer();
	btAlignedObjectArray<btVector3> vertices, chverts;
	btVector3 rbb, nrbb;
	btScalar nlength, maxDistance, distance;
	btAlignedObjectArray<btVector3> sortedVoronoiPoints;
	sortedVoronoiPoints.copyFromArray(points);
	btVector3 normal, plane;
	btAlignedObjectArray<btVector3> planes, convexPlanes;
	std::set<int> planeIndices;
	std::set<int>::iterator planeIndicesIter;
	int numplaneIndices;
	int cellnum = 0;
	int i, j, k;

	// Convert verts to world space and get convexPlanes
	int numverts = verts.size();
	chverts.resize(verts.size());
	for (i=0; i < numverts ;i++) {
		chverts[i] = quatRotate(bbq, verts[i]) + bbt;
	}
	//btGeometryUtil::getPlaneEquationsFromVertices(chverts, convexPlanes);
	// Using convexHullComputer faster than getPlaneEquationsFromVertices for large meshes...
	convexHC->compute(&chverts[0].getX(), sizeof(btVector3), numverts, 0.0, 0.0);
	int numFaces = convexHC->faces.size();
	int v0, v1, v2; // vertices
	for (i=0; i < numFaces; i++) {
		const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[i]];
		v0 = edge->getSourceVertex();
		v1 = edge->getTargetVertex();
		edge = edge->getNextEdgeOfFace();
		v2 = edge->getTargetVertex();
		plane = (convexHC->vertices[v1]-convexHC->vertices[v0]).cross(convexHC->vertices[v2]-convexHC->vertices[v0]).normalize();
		plane[3] = -plane.dot(convexHC->vertices[v0]);
		convexPlanes.push_back(plane);
	}
	const int numconvexPlanes = convexPlanes.size();

	int numpoints = points.size();
	for (i=0; i < numpoints ;i++) {
		curVoronoiPoint = points[i];
		planes.copyFromArray(convexPlanes);
		for (j=0; j < numconvexPlanes ;j++) {
			planes[j][3] += planes[j].dot(curVoronoiPoint);
		}
		maxDistance = SIMD_INFINITY;
		sortedVoronoiPoints.heapSort(pointCmp());
		for (j=1; j < numpoints; j++) {
			normal = sortedVoronoiPoints[j] - curVoronoiPoint;
			nlength = normal.length();
			if (nlength > maxDistance)
				break;
			plane = normal.normalized();
			plane[3] = -nlength / btScalar(2.);
			planes.push_back(plane);
			getVerticesInsidePlanes(planes, vertices, planeIndices);
			if (vertices.size() == 0)
				break;
			numplaneIndices = planeIndices.size();
			if (numplaneIndices != planes.size()) {
				planeIndicesIter = planeIndices.begin();
				for (k=0; k < numplaneIndices; k++) {
					if (k != *planeIndicesIter)
						planes[k] = planes[*planeIndicesIter];
					planeIndicesIter++;
				}
				planes.resize(numplaneIndices);
			}
			maxDistance = vertices[0].length();
			for (k=1; k < vertices.size(); k++) {
				distance = vertices[k].length();
				if (maxDistance < distance)
					maxDistance = distance;
			}
			maxDistance *= btScalar(2.);
		}
		if (vertices.size() == 0)
			continue;

		// Clean-up voronoi convex shard vertices and generate edges & faces
		convexHC->compute(&vertices[0].getX(), sizeof(btVector3), vertices.size(),0.0,0.0);

		// At this point we have a complete 3D voronoi shard mesh contained in convexHC

		// Calculate volume and center of mass (Stan Melax volume integration)
		numFaces = convexHC->faces.size();
		btScalar volume = btScalar(0.);
		btVector3 com(0., 0., 0.);
		for (j=0; j < numFaces; j++) {
			const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[j]];
			v0 = edge->getSourceVertex();
			v1 = edge->getTargetVertex();
			edge = edge->getNextEdgeOfFace();
			v2 = edge->getTargetVertex();
			while (v2 != v0) {
				// Counter-clockwise triangulated voronoi shard mesh faces (v0-v1-v2) and edges here...
				btScalar vol = convexHC->vertices[v0].triple(convexHC->vertices[v1], convexHC->vertices[v2]);
				volume += vol;
				com += vol * (convexHC->vertices[v0] + convexHC->vertices[v1] + convexHC->vertices[v2]);
				edge = edge->getNextEdgeOfFace();
				v1 = v2;
				v2 = edge->getTargetVertex();
			}
		}
		com /= volume * btScalar(4.);
		volume /= btScalar(6.);

		// Shift all vertices relative to center of mass
		int numVerts = convexHC->vertices.size();
		for (j=0; j < numVerts; j++)
		{
			convexHC->vertices[j] -= com;
		}

		// Note:
		// At this point convex hulls contained in convexHC should be accurate (line up flush with other pieces, no cracks),
		// ...however Bullet Physics rigid bodies demo visualizations appear to produce some visible cracks.
		// Use the mesh in convexHC for visual display or to perform boolean operations with.

		// Create Bullet Physics rigid body shards
		btCollisionShape* shardShape = new btConvexHullShape(&(convexHC->vertices[0].getX()), convexHC->vertices.size());
		shardShape->setMargin(CONVEX_MARGIN); // for this demo; note convexHC has optional margin parameter for this
		m_collisionShapes.push_back(shardShape);
		btTransform shardTransform;
		shardTransform.setIdentity();
		shardTransform.setOrigin(curVoronoiPoint + com); // Shard's adjusted location
		btDefaultMotionState* shardMotionState = new btDefaultMotionState(shardTransform);
		btScalar shardMass(volume * matDensity);
		btVector3 shardInertia(0.,0.,0.);
		shardShape->calculateLocalInertia(shardMass, shardInertia);
		btRigidBody::btRigidBodyConstructionInfo shardRBInfo(shardMass, shardMotionState, shardShape, shardInertia);
		btRigidBody* shardBody = new btRigidBody(shardRBInfo);
		m_dynamicsWorld->addRigidBody(shardBody);

		cellnum ++;

	}
	printf("Generated %d voronoi btRigidBody shards\n", cellnum);
}
Example #2
0
    std::list<VoronoiShard>
    voronoi_convex_hull_shatter(const gl::MeshPtr &the_mesh,
                                const std::vector<glm::vec3>& the_voronoi_points)
    {
        // points define voronoi cells in world space (avoid duplicates)
        // verts = source (convex hull) mesh vertices in local space
        
        std::list<VoronoiShard> ret;
        std::vector<glm::vec3> mesh_verts = the_mesh->geometry()->vertices();
        
        auto convexHC = std::make_shared<btConvexHullComputer>();
        btAlignedObjectArray<btVector3> vertices;
        
        btVector3 rbb, nrbb;
        btScalar nlength, maxDistance, distance;
        std::vector<glm::vec3> sortedVoronoiPoints = the_voronoi_points;
        
        btVector3 normal, plane;
        btAlignedObjectArray<btVector3> planes, convexPlanes;
        std::set<int> planeIndices;
        std::set<int>::iterator planeIndicesIter;
        int numplaneIndices;
        int i, j, k;
        
        // Normalize verts (numerical stability), convert to world space and get convexPlanes
        int numverts = mesh_verts.size();
//        auto aabb = the_mesh->boundingBox();
//        float scale_val = 1.f;//std::max(std::max(aabb.width(), aabb.height()), aabb.depth());
        
        auto mesh_transform = the_mesh->global_transform() ;//* scale(glm::mat4(), vec3(1.f / scale_val));
        std::vector<glm::vec3> world_space_verts;
        
        world_space_verts.resize(mesh_verts.size());
        for (i = 0; i < numverts ;i++)
        {
            world_space_verts[i] = (mesh_transform * vec4(mesh_verts[i], 1.f)).xyz();
        }
        
        //btGeometryUtil::getPlaneEquationsFromVertices(chverts, convexPlanes);
        // Using convexHullComputer faster than getPlaneEquationsFromVertices for large meshes...
        convexHC->compute(&world_space_verts[0].x, sizeof(world_space_verts[0]), numverts, 0.0, 0.0);
        
        int numFaces = convexHC->faces.size();
        int v0, v1, v2; // vertices
        
        // get plane equations for the convex-hull n-gons
        for (i = 0; i < numFaces; i++)
        {
            const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[i]];
            v0 = edge->getSourceVertex();
            v1 = edge->getTargetVertex();
            edge = edge->getNextEdgeOfFace();
            v2 = edge->getTargetVertex();
            plane = (convexHC->vertices[v1]-convexHC->vertices[v0]).cross(convexHC->vertices[v2]-convexHC->vertices[v0]).normalize();
            plane[3] = -plane.dot(convexHC->vertices[v0]);
            convexPlanes.push_back(plane);
        }
        const int numconvexPlanes = convexPlanes.size();
        
        int numpoints = the_voronoi_points.size();
        
        for (i = 0; i < numpoints ; i++)
        {
            auto curVoronoiPoint = the_voronoi_points[i];
            planes.copyFromArray(convexPlanes);
            
            for (j = 0; j < numconvexPlanes; j++)
            {
                planes[j][3] += planes[j].dot(type_cast(the_voronoi_points[i]));
            }
            maxDistance = SIMD_INFINITY;
            
            // sort voronoi points
            std::sort(sortedVoronoiPoints.begin(), sortedVoronoiPoints.end(), pointCmp(curVoronoiPoint));
            
            for (j=1; j < numpoints; j++)
            {
                normal = type_cast(sortedVoronoiPoints[j] - curVoronoiPoint);
                nlength = normal.length();
                if (nlength > maxDistance)
                    break;
                plane = normal.normalized();
                plane[3] = -nlength / btScalar(2.);
                planes.push_back(plane);
                getVerticesInsidePlanes(planes, vertices, planeIndices);
                
                if (vertices.size() == 0) break;
                
                numplaneIndices = planeIndices.size();
                if (numplaneIndices != planes.size())
                {
                    planeIndicesIter = planeIndices.begin();
                    for (k=0; k < numplaneIndices; k++)
                    {
                        if (k != *planeIndicesIter)
                            planes[k] = planes[*planeIndicesIter];
                        planeIndicesIter++;
                    }
                    planes.resize(numplaneIndices);
                }
                maxDistance = vertices[0].length();
                for (k=1; k < vertices.size(); k++)
                {
                    distance = vertices[k].length();
                    if (maxDistance < distance)
                        maxDistance = distance;
                }
                maxDistance *= btScalar(2.);
            }
            if (vertices.size() == 0)
                continue;
            
            // Clean-up voronoi convex shard vertices and generate edges & faces
            convexHC->compute(&vertices[0].getX(), sizeof(btVector3), vertices.size(),0.0,0.0);
            
            // At this point we have a complete 3D voronoi shard mesh contained in convexHC
            
            // Calculate volume and center of mass (Stan Melax volume integration)
            numFaces = convexHC->faces.size();
            btScalar volume = btScalar(0.);
            btVector3 com(0., 0., 0.);
            
            for (j = 0; j < numFaces; j++)
            {
                const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[j]];
                v0 = edge->getSourceVertex();
                v1 = edge->getTargetVertex();
                edge = edge->getNextEdgeOfFace();
                v2 = edge->getTargetVertex();
                
                while (v2 != v0)
                {
                    // Counter-clockwise triangulated voronoi shard mesh faces (v0-v1-v2) and edges here...
                    btScalar vol = convexHC->vertices[v0].triple(convexHC->vertices[v1], convexHC->vertices[v2]);
                    volume += vol;
                    com += vol * (convexHC->vertices[v0] + convexHC->vertices[v1] + convexHC->vertices[v2]);
                    edge = edge->getNextEdgeOfFace();
                    
                    v1 = v2;
                    v2 = edge->getTargetVertex();
                }
            }
            com /= volume * btScalar(4.);
            volume /= btScalar(6.);
            
            // Shift all vertices relative to center of mass
            int numVerts = convexHC->vertices.size();
            for (j = 0; j < numVerts; j++)
            {
                convexHC->vertices[j] -= com;
            }
            
            // now create our output geometry with indices
            std::vector<gl::Face3> outer_faces, inner_faces;
            std::vector<glm::vec3> outer_vertices, inner_vertices;
            int cur_outer_index = 0, cur_inner_index = 0;
            
            for (j = 0; j < numFaces; j++)
            {
                const btConvexHullComputer::Edge* edge = &convexHC->edges[convexHC->faces[j]];
                v0 = edge->getSourceVertex();
                v1 = edge->getTargetVertex();
                edge = edge->getNextEdgeOfFace();
                v2 = edge->getTargetVertex();
                
                // determine if it is an inner or outer face
                btVector3 cur_plane = (convexHC->vertices[v1] - convexHC->vertices[v0]).cross(convexHC->vertices[v2]-convexHC->vertices[v0]).normalize();
                cur_plane[3] = -cur_plane.dot(convexHC->vertices[v0]);
                bool is_outside = false;
                
                for(uint32_t q = 0; q < convexPlanes.size(); q++)
                {
                    if(is_equal(convexPlanes[q], cur_plane, 0.01f)){ is_outside = true; break;}
                }
                std::vector<gl::Face3> *shard_faces = &outer_faces;
                std::vector<glm::vec3> *shard_vertices = &outer_vertices;
                int *shard_index = &cur_outer_index;
                
                if(!is_outside)
                {
                    shard_faces = &inner_faces;
                    shard_vertices = &inner_vertices;
                    shard_index = &cur_inner_index;
                }
                
                int face_start_index = *shard_index;
                
                // advance index
                *shard_index += 3;
                
                // first 3 verts of n-gon
                glm::vec3 tmp[] = { type_cast(convexHC->vertices[v0]),
                                    type_cast(convexHC->vertices[v1]),
                                    type_cast(convexHC->vertices[v2])};
                
                shard_vertices->insert(shard_vertices->end(), tmp, tmp + 3);
                shard_faces->push_back(gl::Face3(face_start_index,
                                                 face_start_index + 1,
                                                 face_start_index + 2));
                
                // add remaining triangles of face (if any)
                while (true)
                {
                    edge = edge->getNextEdgeOfFace();
                    v1 = v2;
                    v2 = edge->getTargetVertex();
                    
                    // end of n-gon
                    if(v2 == v0) break;
                    
                    shard_vertices->push_back(type_cast(convexHC->vertices[v2]));
                    shard_faces->push_back(gl::Face3(face_start_index,
                                                     *shard_index - 1,
                                                     *shard_index));
                    (*shard_index)++;
                }
            }
            
            // entry construction
            gl::Mesh::Entry e0, e1;
            
            // outer entry
            e0.num_vertices = outer_vertices.size();
            e0.num_indices = outer_faces.size() * 3;
            e0.material_index = 0;
            
            // inner entry
            e1.base_index = e0.num_indices;
            e1.base_vertex = e0.num_vertices;
            e1.num_vertices = inner_vertices.size();
            e1.num_indices = inner_faces.size() * 3;
            e1.material_index = 1;
            
            // create gl::Mesh object for the shard
            auto inner_geom = gl::Geometry::create(), outer_geom = gl::Geometry::create();
            
            // append verts and indices
            outer_geom->append_faces(outer_faces);
            outer_geom->vertices() = outer_vertices;
            outer_geom->compute_face_normals();
            
            inner_geom->append_faces(inner_faces);
            inner_geom->append_vertices(inner_vertices);
            inner_geom->compute_face_normals();
            
            // merge geometries
            outer_geom->append_vertices(inner_geom->vertices());
            outer_geom->append_normals(inner_geom->normals());
            outer_geom->append_indices(inner_geom->indices());
            outer_geom->faces().insert(outer_geom->faces().end(),
                                       inner_geom->faces().begin(), inner_geom->faces().end());
            outer_geom->compute_bounding_box();
            
            auto inner_mat = gl::Material::create();
            
            auto m = gl::Mesh::create(outer_geom, gl::Material::create());
            m->entries() = {e0, e1};
            m->materials().push_back(inner_mat);
            m->set_position(curVoronoiPoint + type_cast(com));
//            m->transform() *= glm::scale(mat4(), vec3(scale_val));
            
            // compute projected texcoords (outside)
            gl::project_texcoords(the_mesh, m);
            
            // compute box mapped texcoords for inside vertices
            auto &indices = m->geometry()->indices();
            auto &vertices = m->geometry()->vertices();
            
            // aabb
            auto out_aabb = the_mesh->bounding_box();
            vec3 aabb_extents = out_aabb.halfExtents() * 2.f;
            
            uint32_t base_vertex = m->entries()[1].base_vertex;
            uint32_t k = m->entries()[1].base_index, kl = k + m->entries()[1].num_indices;
            
            for(;k < kl; k += 3)
            {
                gl::Face3 f(indices[k] + base_vertex,
                            indices[k] + base_vertex + 1,
                            indices[k] + base_vertex + 2);
                
                // normal
                const vec3 &v0 = vertices[f.a];
                const vec3 &v1 = vertices[f.b];
                const vec3 &v2 = vertices[f.c];
                
                vec3 n = glm::normalize(glm::cross(v1 - v0, v2 - v0));
                
                float abs_vals[3] = {fabsf(n[0]), fabsf(n[1]), fabsf(n[2])};
                
                // get principal direction
                int principle_axis = std::distance(abs_vals, std::max_element(abs_vals, abs_vals + 3));
                
//                switch (principle_axis)
//                {
//                    // X-axis
//                    case 0:
//                        //ZY plane
//                        m->geometry()->texCoords()[f.a] = vec2(v0.z - out_aabb.min.z / aabb_extents.z,
//                                                               v0.y - out_aabb.min.y / aabb_extents.y);
//                        m->geometry()->texCoords()[f.b] = vec2(v1.z - out_aabb.min.z / aabb_extents.z,
//                                                               v1.y - out_aabb.min.y / aabb_extents.y);
//                        m->geometry()->texCoords()[f.c] = vec2(v2.z - out_aabb.min.z / aabb_extents.z,
//                                                               v2.y - out_aabb.min.y / aabb_extents.y);
//                        break;
//                        
//                    // Y-axis
//                    case 1:
//                    // XZ plane
//                        m->geometry()->texCoords()[f.a] = vec2(v0.x - out_aabb.min.x / aabb_extents.x,
//                                                               v0.z - out_aabb.min.z / aabb_extents.z);
//                        m->geometry()->texCoords()[f.b] = vec2(v1.x - out_aabb.min.x / aabb_extents.x,
//                                                               v1.z - out_aabb.min.z / aabb_extents.z);
//                        m->geometry()->texCoords()[f.c] = vec2(v2.x - out_aabb.min.x / aabb_extents.x,
//                                                               v2.z - out_aabb.min.z / aabb_extents.z);
//                        break;
//                        
//                    // Z-axis
//                    case 2:
//                        //XY plane
//                        m->geometry()->texCoords()[f.a] = vec2(v0.x - out_aabb.min.x / aabb_extents.x,
//                                                               v0.y - out_aabb.min.y / aabb_extents.y);
//                        m->geometry()->texCoords()[f.b] = vec2(v1.x - out_aabb.min.x / aabb_extents.x,
//                                                               v1.y - out_aabb.min.y / aabb_extents.y);
//                        m->geometry()->texCoords()[f.c] = vec2(v2.x - out_aabb.min.x / aabb_extents.x,
//                                                               v2.y - out_aabb.min.y / aabb_extents.y);
//                        break;
//                        
//                    default:
//                        break;
//                }
            }
            // push to return structure
            ret.push_back({m, volume});
        }
        LOG_DEBUG << "Generated " << ret.size() <<" voronoi shards";
        return ret;
    }