/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2010 by the Blender Foundation. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Ken Hughes, * Sergey Sharybin. * * ***** END GPL LICENSE BLOCK ***** */ /** \file bsp/intern/BOP_CarveInterface.cpp * \ingroup bsp */ #include "BOP_Interface.h" #include "BSP_CSGMesh_CFIterator.h" #include #include #include #include using namespace carve::mesh; using namespace carve::geom; typedef unsigned int uint; #define MAX(x,y) ((x)>(y)?(x):(y)) #define MIN(x,y) ((x)<(y)?(x):(y)) static bool isQuadPlanar(carve::geom3d::Vector &v1, carve::geom3d::Vector &v2, carve::geom3d::Vector &v3, carve::geom3d::Vector &v4) { carve::geom3d::Vector vec1, vec2, vec3, cross; vec1 = v2 - v1; vec2 = v4 - v1; vec3 = v3 - v1; cross = carve::geom::cross(vec1, vec2); float production = carve::geom::dot(cross, vec3); float magnitude = 1e-6 * cross.length(); return fabs(production) < magnitude; } static bool isFacePlanar(CSG_IFace &face, std::vector &vertices) { if (face.vertex_number == 4) { return isQuadPlanar(vertices[face.vertex_index[0]], vertices[face.vertex_index[1]], vertices[face.vertex_index[2]], vertices[face.vertex_index[3]]); } return true; } static void Carve_copyMeshes(std::vector::mesh_t*> &meshes, std::vector::mesh_t*> &new_meshes) { std::vector::mesh_t*>::iterator it = meshes.begin(); for(; it!=meshes.end(); it++) { MeshSet<3>::mesh_t *mesh = *it; MeshSet<3>::mesh_t *new_mesh = new MeshSet<3>::mesh_t(mesh->faces); new_meshes.push_back(new_mesh); } } static MeshSet<3> *Carve_meshSetFromMeshes(std::vector::mesh_t*> &meshes) { std::vector::mesh_t*> new_meshes; Carve_copyMeshes(meshes, new_meshes); return new MeshSet<3>(new_meshes); } static MeshSet<3> *Carve_meshSetFromTwoMeshes(std::vector::mesh_t*> &left_meshes, std::vector::mesh_t*> &right_meshes) { std::vector::mesh_t*> new_meshes; Carve_copyMeshes(left_meshes, new_meshes); Carve_copyMeshes(right_meshes, new_meshes); return new MeshSet<3>(new_meshes); } static bool Carve_checkEdgeFaceIntersections_do(carve::csg::Intersections &intersections, MeshSet<3>::face_t *face_a, MeshSet<3>::edge_t *edge_b) { if(intersections.intersects(edge_b, face_a)) return true; carve::mesh::MeshSet<3>::vertex_t::vector_t _p; if(face_a->simpleLineSegmentIntersection(carve::geom3d::LineSegment(edge_b->v1()->v, edge_b->v2()->v), _p)) return true; return false; } static bool Carve_checkEdgeFaceIntersections(carve::csg::Intersections &intersections, MeshSet<3>::face_t *face_a, MeshSet<3>::face_t *face_b) { MeshSet<3>::edge_t *edge_b; edge_b = face_b->edge; do { if(Carve_checkEdgeFaceIntersections_do(intersections, face_a, edge_b)) return true; edge_b = edge_b->next; } while (edge_b != face_b->edge); return false; } static inline bool Carve_facesAreCoplanar(const MeshSet<3>::face_t *a, const MeshSet<3>::face_t *b) { carve::geom3d::Ray temp; // XXX: Find a better definition. This may be a source of problems // if floating point inaccuracies cause an incorrect answer. return !carve::geom3d::planeIntersection(a->plane, b->plane, temp); } static bool Carve_checkMeshSetInterseciton_do(carve::csg::Intersections &intersections, const RTreeNode<3, Face<3> *> *a_node, const RTreeNode<3, Face<3> *> *b_node, bool descend_a = true) { if(!a_node->bbox.intersects(b_node->bbox)) return false; if(a_node->child && (descend_a || !b_node->child)) { for(RTreeNode<3, Face<3> *> *node = a_node->child; node; node = node->sibling) { if(Carve_checkMeshSetInterseciton_do(intersections, node, b_node, false)) return true; } } else if(b_node->child) { for(RTreeNode<3, Face<3> *> *node = b_node->child; node; node = node->sibling) { if(Carve_checkMeshSetInterseciton_do(intersections, a_node, node, true)) return true; } } else { for(size_t i = 0; i < a_node->data.size(); ++i) { MeshSet<3>::face_t *fa = a_node->data[i]; aabb<3> aabb_a = fa->getAABB(); if(aabb_a.maxAxisSeparation(b_node->bbox) > carve::EPSILON) continue; for(size_t j = 0; j < b_node->data.size(); ++j) { MeshSet<3>::face_t *fb = b_node->data[j]; aabb<3> aabb_b = fb->getAABB(); if(aabb_b.maxAxisSeparation(aabb_a) > carve::EPSILON) continue; std::pair a_ra = fa->rangeInDirection(fa->plane.N, fa->edge->vert->v); std::pair b_ra = fb->rangeInDirection(fa->plane.N, fa->edge->vert->v); if(carve::rangeSeparation(a_ra, b_ra) > carve::EPSILON) continue; std::pair a_rb = fa->rangeInDirection(fb->plane.N, fb->edge->vert->v); std::pair b_rb = fb->rangeInDirection(fb->plane.N, fb->edge->vert->v); if(carve::rangeSeparation(a_rb, b_rb) > carve::EPSILON) continue; if(!Carve_facesAreCoplanar(fa, fb)) { if(Carve_checkEdgeFaceIntersections(intersections, fa, fb)) { return true; } } } } } return false; } static bool Carve_checkMeshSetInterseciton(RTreeNode<3, Face<3> *> *rtree_a, RTreeNode<3, Face<3> *> *rtree_b) { carve::csg::Intersections intersections; return Carve_checkMeshSetInterseciton_do(intersections, rtree_a, rtree_b); } static void Carve_getIntersectedOperandMeshes(std::vector::mesh_t*> &meshes, MeshSet<3>::aabb_t &otherAABB, std::vector::mesh_t*> &operandMeshes) { std::vector::mesh_t*>::iterator it = meshes.begin(); std::vector< RTreeNode<3, Face<3> *> *> meshRTree; while(it != meshes.end()) { MeshSet<3>::mesh_t *mesh = *it; bool isAdded = false; RTreeNode<3, Face<3> *> *rtree = RTreeNode<3, Face<3> *>::construct_STR(mesh->faces.begin(), mesh->faces.end(), 4, 4); if (rtree->bbox.intersects(otherAABB)) { bool isIntersect = false; std::vector::mesh_t*>::iterator operand_it = operandMeshes.begin(); std::vector *> *>::iterator tree_it = meshRTree.begin(); for(; operand_it!=operandMeshes.end(); operand_it++, tree_it++) { RTreeNode<3, Face<3> *> *operandRTree = *tree_it; if(Carve_checkMeshSetInterseciton(rtree, operandRTree)) { isIntersect = true; break; } } if(!isIntersect) { operandMeshes.push_back(mesh); meshRTree.push_back(rtree); it = meshes.erase(it); isAdded = true; } } if (!isAdded) { delete rtree; it++; } } std::vector *> *>::iterator tree_it = meshRTree.begin(); for(; tree_it != meshRTree.end(); tree_it++) { delete *tree_it; } } static MeshSet<3> *Carve_getIntersectedOperand(std::vector::mesh_t*> &meshes, MeshSet<3>::aabb_t &otherAABB) { std::vector::mesh_t*> operandMeshes; Carve_getIntersectedOperandMeshes(meshes, otherAABB, operandMeshes); if (operandMeshes.size() == 0) return NULL; return Carve_meshSetFromMeshes(operandMeshes); } static MeshSet<3> *Carve_unionIntersectingMeshes(MeshSet<3> *poly, MeshSet<3>::aabb_t &otherAABB, carve::interpolate::FaceAttr &oface_num) { if(poly->meshes.size()<=1) return poly; carve::csg::CSG csg; oface_num.installHooks(csg); csg.hooks.registerHook(new carve::csg::CarveTriangulator, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); std::vector::mesh_t*> orig_meshes = std::vector::mesh_t*>(poly->meshes.begin(), poly->meshes.end()); MeshSet<3> *left = Carve_getIntersectedOperand(orig_meshes, otherAABB); if (!left) { /* no maniforlds which intersects another object at all */ return poly; } while(orig_meshes.size()) { MeshSet<3> *right = Carve_getIntersectedOperand(orig_meshes, otherAABB); if (!right) { /* no more intersecting manifolds which intersects other object */ break; } try { if(left->meshes.size()==0) { delete left; left = right; } else { MeshSet<3> *result = csg.compute(left, right, carve::csg::CSG::UNION, NULL, carve::csg::CSG::CLASSIFY_EDGE); delete left; delete right; left = result; } } catch(carve::exception e) { std::cerr << "CSG failed, exception " << e.str() << std::endl; MeshSet<3> *result = Carve_meshSetFromTwoMeshes(left->meshes, right->meshes); delete left; delete right; left = result; } catch(...) { delete left; delete right; throw "Unknown error in Carve library"; } } /* append all meshes which doesn't have intersection with another operand as-is */ if (orig_meshes.size()) { MeshSet<3> *result = Carve_meshSetFromTwoMeshes(left->meshes, orig_meshes); delete left; return result; } return left; } static void Carve_unionIntersections(MeshSet<3> **left_r, MeshSet<3> **right_r, carve::interpolate::FaceAttr &oface_num) { MeshSet<3> *left, *right; MeshSet<3>::aabb_t leftAABB = (*left_r)->getAABB(); MeshSet<3>::aabb_t rightAABB = (*right_r)->getAABB(); left = Carve_unionIntersectingMeshes(*left_r, rightAABB, oface_num); right = Carve_unionIntersectingMeshes(*right_r, leftAABB, oface_num); if(left != *left_r) delete *left_r; if(right != *right_r) delete *right_r; *left_r = left; *right_r = right; } static MeshSet<3> *Carve_addMesh(CSG_FaceIteratorDescriptor &face_it, CSG_VertexIteratorDescriptor &vertex_it, carve::interpolate::FaceAttr &oface_num, uint &num_origfaces) { CSG_IVertex vertex; std::vector vertices; while (!vertex_it.Done(vertex_it.it)) { vertex_it.Fill(vertex_it.it,&vertex); vertices.push_back(VECTOR(vertex.position[0], vertex.position[1], vertex.position[2])); vertex_it.Step(vertex_it.it); } CSG_IFace face; std::vector f; int numfaces = 0; // now for the polygons. // we may need to decalare some memory for user defined face properties. std::vector forig; while (!face_it.Done(face_it.it)) { face_it.Fill(face_it.it,&face); if (isFacePlanar(face, vertices)) { f.push_back(face.vertex_number); f.push_back(face.vertex_index[0]); f.push_back(face.vertex_index[1]); f.push_back(face.vertex_index[2]); if (face.vertex_number == 4) f.push_back(face.vertex_index[3]); forig.push_back(face.orig_face); ++numfaces; face_it.Step(face_it.it); ++num_origfaces; } else { f.push_back(3); f.push_back(face.vertex_index[0]); f.push_back(face.vertex_index[1]); f.push_back(face.vertex_index[2]); forig.push_back(face.orig_face); ++numfaces; if (face.vertex_number == 4) { f.push_back(3); f.push_back(face.vertex_index[0]); f.push_back(face.vertex_index[2]); f.push_back(face.vertex_index[3]); forig.push_back(face.orig_face); ++numfaces; } face_it.Step(face_it.it); ++num_origfaces; } } MeshSet<3> *poly = new MeshSet<3> (vertices, numfaces, f); uint i; MeshSet<3>::face_iter face_iter = poly->faceBegin(); for (i = 0; face_iter != poly->faceEnd(); ++face_iter, ++i) { MeshSet<3>::face_t *face = *face_iter; oface_num.setAttribute(face, forig[i]); } return poly; } static double triangleArea(carve::geom3d::Vector &v1, carve::geom3d::Vector &v2, carve::geom3d::Vector &v3) { carve::geom3d::Vector a = v2 - v1; carve::geom3d::Vector b = v3 - v1; return carve::geom::cross(a, b).length(); } static bool checkValidQuad(std::vector::vertex_t> &vertex_storage, uint quad[4]) { carve::geom3d::Vector &v1 = vertex_storage[quad[0]].v; carve::geom3d::Vector &v2 = vertex_storage[quad[1]].v; carve::geom3d::Vector &v3 = vertex_storage[quad[2]].v; carve::geom3d::Vector &v4 = vertex_storage[quad[3]].v; #if 0 /* disabled for now to prevent initially non-planar be triangulated * in theory this might cause some artifacts if intersections happens by non-planar * non-concave quad, but in practice it's acceptable */ if (!isQuadPlanar(v1, v2, v3, v4)) { /* non-planar faces better not be merged because of possible differences in triangulation * of non-planar faces in opengl and renderer */ return false; } #endif carve::geom3d::Vector edges[4]; carve::geom3d::Vector normal; bool normal_set = false; edges[0] = v2 - v1; edges[1] = v3 - v2; edges[2] = v4 - v3; edges[3] = v1 - v4; for (int i = 0; i < 4; i++) { int n = i + 1; if (n == 4) n = 0; carve::geom3d::Vector current_normal = carve::geom::cross(edges[i], edges[n]); if (current_normal.length() > DBL_EPSILON) { if (!normal_set) { normal = current_normal; normal_set = true; } else if (carve::geom::dot(normal, current_normal) < 0) { return false; } } } if (!normal_set) { /* normal wasn't set means face is degraded and better merge it in such way */ return false; } double area = triangleArea(v1, v2, v3) + triangleArea(v1, v3, v4); if (area <= DBL_EPSILON) return false; return true; } // check whether two faces share an edge, and if so merge them static uint quadMerge(std::map::vertex_t*, uint> *vertexToIndex_map, std::vector::vertex_t> &vertex_storage, MeshSet<3>::face_t *f1, MeshSet<3>::face_t *f2, uint v, uint quad[4]) { uint current, n1, p1, n2, p2; uint v1[3]; uint v2[3]; // get the vertex indices for each face v1[0] = vertexToIndex_map->find(f1->edge->vert)->second; v1[1] = vertexToIndex_map->find(f1->edge->next->vert)->second; v1[2] = vertexToIndex_map->find(f1->edge->next->next->vert)->second; v2[0] = vertexToIndex_map->find(f2->edge->vert)->second; v2[1] = vertexToIndex_map->find(f2->edge->next->vert)->second; v2[2] = vertexToIndex_map->find(f2->edge->next->next->vert)->second; // locate the current vertex we're examining, and find the next and // previous vertices based on the face windings if (v1[0] == v) {current = 0; p1 = 2; n1 = 1;} else if (v1[1] == v) {current = 1; p1 = 0; n1 = 2;} else {current = 2; p1 = 1; n1 = 0;} if (v2[0] == v) {p2 = 2; n2 = 1;} else if (v2[1] == v) {p2 = 0; n2 = 2;} else {p2 = 1; n2 = 0;} // if we find a match, place indices into quad in proper order and return // success code if (v1[p1] == v2[n2]) { quad[0] = v1[current]; quad[1] = v1[n1]; quad[2] = v1[p1]; quad[3] = v2[p2]; return checkValidQuad(vertex_storage, quad); } else if (v1[n1] == v2[p2]) { quad[0] = v1[current]; quad[1] = v2[n2]; quad[2] = v1[n1]; quad[3] = v1[p1]; return checkValidQuad(vertex_storage, quad); } return 0; } static bool Carve_checkDegeneratedFace(std::map::vertex_t*, uint> *vertexToIndex_map, MeshSet<3>::face_t *face) { /* only tris and quads for now */ if (face->n_edges == 3) { uint v1, v2, v3; v1 = vertexToIndex_map->find(face->edge->prev->vert)->second; v2 = vertexToIndex_map->find(face->edge->vert)->second; v3 = vertexToIndex_map->find(face->edge->next->vert)->second; if (v1 == v2 || v2 == v3 || v1 == v3) return true; return triangleArea(face->edge->prev->vert->v, face->edge->vert->v, face->edge->next->vert->v) < DBL_EPSILON; } else if (face->n_edges == 4) { uint v1, v2, v3, v4; v1 = vertexToIndex_map->find(face->edge->prev->vert)->second; v2 = vertexToIndex_map->find(face->edge->vert)->second; v3 = vertexToIndex_map->find(face->edge->next->vert)->second; v4 = vertexToIndex_map->find(face->edge->next->next->vert)->second; if (v1 == v2 || v1 == v3 || v1 == v4 || v2 == v3 || v2 == v4 || v3 == v4) return true; return triangleArea(face->edge->vert->v, face->edge->next->vert->v, face->edge->next->next->vert->v) + triangleArea(face->edge->prev->vert->v, face->edge->vert->v, face->edge->next->next->vert->v) < DBL_EPSILON; } return false; } static BSP_CSGMesh *Carve_exportMesh(MeshSet<3>* &poly, carve::interpolate::FaceAttr &oface_num, uint num_origfaces) { uint i; BSP_CSGMesh *outputMesh = BSP_CSGMesh::New(); if (outputMesh == NULL) return NULL; std::vector *vertices = new std::vector; outputMesh->SetVertices(vertices); std::map::vertex_t*, uint> vertexToIndex_map; std::vector::vertex_t>::iterator it = poly->vertex_storage.begin(); for (i = 0; it != poly->vertex_storage.end(); ++i, ++it) { MeshSet<3>::vertex_t *vertex = &(*it); vertexToIndex_map[vertex] = i; } for (i = 0; i < poly->vertex_storage.size(); ++i ) { BSP_MVertex outVtx(MT_Point3 (poly->vertex_storage[i].v[0], poly->vertex_storage[i].v[1], poly->vertex_storage[i].v[2])); outVtx.m_edges.clear(); outputMesh->VertexSet().push_back(outVtx); } // build vectors of faces for each original face and each vertex std::vector > vi(poly->vertex_storage.size()); std::vector > ofaces(num_origfaces); MeshSet<3>::face_iter face_iter = poly->faceBegin(); for (i = 0; face_iter != poly->faceEnd(); ++face_iter, ++i) { MeshSet<3>::face_t *f = *face_iter; if (Carve_checkDegeneratedFace(&vertexToIndex_map, f)) continue; ofaces[oface_num.getAttribute(f)].push_back(i); MeshSet<3>::face_t::edge_iter_t edge_iter = f->begin(); for (; edge_iter != f->end(); ++edge_iter) { int index = vertexToIndex_map[edge_iter->vert]; vi[index].push_back(i); } } uint quadverts[4] = {0, 0, 0, 0}; // go over each set of faces which belong to an original face std::vector< std::vector >::const_iterator fii; uint orig = 0; for (fii=ofaces.begin(); fii!=ofaces.end(); ++fii, ++orig) { std::vector fl = *fii; // go over a single set from an original face while (fl.size() > 0) { // remove one new face uint findex = fl.back(); fl.pop_back(); MeshSet<3>::face_t *f = *(poly->faceBegin() + findex); // for each vertex of this face, check other faces containing // that vertex to see if there is a neighbor also belonging to // the original face uint result = 0; MeshSet<3>::face_t::edge_iter_t edge_iter = f->begin(); for (; edge_iter != f->end(); ++edge_iter) { int v = vertexToIndex_map[edge_iter->vert]; for (uint pos2=0; !result && pos2 < vi[v].size();pos2++) { // if we find the current face, ignore it uint otherf = vi[v][pos2]; if (findex == otherf) continue; MeshSet<3>::face_t *f2 = *(poly->faceBegin() + otherf); // if other face doesn't have the same original face, // ignore it also uint other_orig = oface_num.getAttribute(f2); if (orig != other_orig) continue; // if, for some reason, we don't find the other face in // the current set of faces, ignore it uint other_index = 0; while (other_index < fl.size() && fl[other_index] != otherf) ++other_index; if (other_index == fl.size()) continue; // see if the faces share an edge result = quadMerge(&vertexToIndex_map, poly->vertex_storage, f, f2, v, quadverts); // if faces can be merged, then remove the other face // from the current set if (result) { uint replace = fl.back(); fl.pop_back(); if(otherf != replace) fl[other_index] = replace; } } } // add all information except vertices to the output mesh outputMesh->FaceSet().push_back(BSP_MFace()); BSP_MFace& outFace = outputMesh->FaceSet().back(); outFace.m_verts.clear(); outFace.m_plane.setValue(f->plane.N.v); outFace.m_orig_face = orig; // if we merged faces, use the list of common vertices; otherwise // use the faces's vertices if (result) { // make quat using verts stored in result outFace.m_verts.push_back(quadverts[0]); outFace.m_verts.push_back(quadverts[1]); outFace.m_verts.push_back(quadverts[2]); outFace.m_verts.push_back(quadverts[3]); } else { MeshSet<3>::face_t::edge_iter_t edge_iter = f->begin(); for (; edge_iter != f->end(); ++edge_iter) { //int index = ofacevert_num.getAttribute(f, edge_iter.idx()); int index = vertexToIndex_map[edge_iter->vert]; outFace.m_verts.push_back( index ); } } } } // Build the mesh edges using topological informtion outputMesh->BuildEdges(); return outputMesh; } /** * Performs a generic booleam operation, the entry point for external modules. * @param opType Boolean operation type BOP_INTERSECTION, BOP_UNION, BOP_DIFFERENCE * @param outputMesh Output mesh, the final result (the object C) * @param obAFaces Object A faces list * @param obAVertices Object A vertices list * @param obBFaces Object B faces list * @param obBVertices Object B vertices list * @param interpFunc Interpolating function * @return operation state: BOP_OK, BOP_NO_SOLID, BOP_ERROR */ BoolOpState BOP_performBooleanOperation(BoolOpType opType, BSP_CSGMesh** outputMesh, CSG_FaceIteratorDescriptor obAFaces, CSG_VertexIteratorDescriptor obAVertices, CSG_FaceIteratorDescriptor obBFaces, CSG_VertexIteratorDescriptor obBVertices) { carve::csg::CSG::OP op; MeshSet<3> *left, *right, *output = NULL; carve::csg::CSG csg; carve::geom3d::Vector min, max; carve::interpolate::FaceAttr oface_num; uint num_origfaces = 0; switch (opType) { case BOP_UNION: op = carve::csg::CSG::UNION; break; case BOP_INTERSECTION: op = carve::csg::CSG::INTERSECTION; break; case BOP_DIFFERENCE: op = carve::csg::CSG::A_MINUS_B; break; default: return BOP_ERROR; } left = Carve_addMesh(obAFaces, obAVertices, oface_num, num_origfaces ); right = Carve_addMesh(obBFaces, obBVertices, oface_num, num_origfaces ); min.x = max.x = left->vertex_storage[0].v.x; min.y = max.y = left->vertex_storage[0].v.y; min.z = max.z = left->vertex_storage[0].v.z; for (uint i = 1; i < left->vertex_storage.size(); ++i) { min.x = MIN(min.x,left->vertex_storage[i].v.x); min.y = MIN(min.y,left->vertex_storage[i].v.y); min.z = MIN(min.z,left->vertex_storage[i].v.z); max.x = MAX(max.x,left->vertex_storage[i].v.x); max.y = MAX(max.y,left->vertex_storage[i].v.y); max.z = MAX(max.z,left->vertex_storage[i].v.z); } for (uint i = 0; i < right->vertex_storage.size(); ++i) { min.x = MIN(min.x,right->vertex_storage[i].v.x); min.y = MIN(min.y,right->vertex_storage[i].v.y); min.z = MIN(min.z,right->vertex_storage[i].v.z); max.x = MAX(max.x,right->vertex_storage[i].v.x); max.y = MAX(max.y,right->vertex_storage[i].v.y); max.z = MAX(max.z,right->vertex_storage[i].v.z); } carve::rescale::rescale scaler(min.x, min.y, min.z, max.x, max.y, max.z); carve::rescale::fwd fwd_r(scaler); carve::rescale::rev rev_r(scaler); left->transform(fwd_r); right->transform(fwd_r); // prepare operands for actual boolean operation. it's needed because operands might consist of // several intersecting meshes and in case if another operands intersect an edge loop of intersecting that // meshes tessellation of operation result can't be done properly. the only way to make such situations // working is to union intersecting meshes of the same operand Carve_unionIntersections(&left, &right, oface_num); if(left->meshes.size() == 0 || right->meshes.size()==0) { // normally sohuldn't happen (zero-faces objects are handled by modifier itself), but // unioning intersecting meshes which doesn't have consistent normals might lead to // empty result which wouldn't work here delete left; delete right; return BOP_ERROR; } csg.hooks.registerHook(new carve::csg::CarveTriangulator, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); oface_num.installHooks(csg); try { output = csg.compute(left, right, op, NULL, carve::csg::CSG::CLASSIFY_EDGE); } catch(carve::exception e) { std::cerr << "CSG failed, exception " << e.str() << std::endl; } catch(...) { delete left; delete right; throw "Unknown error in Carve library"; } delete left; delete right; if(!output) return BOP_ERROR; output->transform(rev_r); *outputMesh = Carve_exportMesh(output, oface_num, num_origfaces); delete output; return BOP_OK; }