blender/extern/carve/carve-util.cc

772 lines
22 KiB
C++
Raw Normal View History

/*
* ***** 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) 2014 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "carve-util.h"
#include "carve-capi.h"
#include <cfloat>
#include <carve/csg.hpp>
#include <carve/csg_triangulator.hpp>
#include <carve/rtree.hpp>
using carve::csg::Intersections;
using carve::geom::aabb;
using carve::geom::RTreeNode;
using carve::geom3d::Vector;
using carve::math::Matrix3;
using carve::mesh::Face;
using carve::mesh::MeshSet;
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
using carve::triangulate::tri_idx;
using carve::triangulate::triangulate;
typedef std::map< MeshSet<3>::mesh_t*, RTreeNode<3, Face<3> *> * > RTreeCache;
typedef std::map< MeshSet<3>::mesh_t*, bool > IntersectCache;
namespace {
// Functions adopted from BLI_math.h to use Carve Vector and Matrix.
void transpose_m3__bli(double mat[3][3])
{
double t;
t = mat[0][1];
mat[0][1] = mat[1][0];
mat[1][0] = t;
t = mat[0][2];
mat[0][2] = mat[2][0];
mat[2][0] = t;
t = mat[1][2];
mat[1][2] = mat[2][1];
mat[2][1] = t;
}
void ortho_basis_v3v3_v3__bli(double r_n1[3], double r_n2[3], const double n[3])
{
const double eps = FLT_EPSILON;
const double f = (n[0] * n[0]) + (n[1] * n[1]);
if (f > eps) {
const double d = 1.0f / sqrt(f);
r_n1[0] = n[1] * d;
r_n1[1] = -n[0] * d;
r_n1[2] = 0.0f;
r_n2[0] = -n[2] * r_n1[1];
r_n2[1] = n[2] * r_n1[0];
r_n2[2] = n[0] * r_n1[1] - n[1] * r_n1[0];
}
else {
/* degenerate case */
r_n1[0] = (n[2] < 0.0f) ? -1.0f : 1.0f;
r_n1[1] = r_n1[2] = r_n2[0] = r_n2[2] = 0.0f;
r_n2[1] = 1.0f;
}
}
void axis_dominant_v3_to_m3__bli(Matrix3 *r_mat, const Vector &normal)
{
memcpy(r_mat->m[2], normal.v, sizeof(double[3]));
ortho_basis_v3v3_v3__bli(r_mat->m[0], r_mat->m[1], r_mat->m[2]);
transpose_m3__bli(r_mat->m);
}
void meshset_minmax(const MeshSet<3> *mesh,
Vector *min,
Vector *max)
{
for (size_t i = 0; i < mesh->vertex_storage.size(); ++i) {
min->x = std::min(min->x, mesh->vertex_storage[i].v.x);
min->y = std::min(min->y, mesh->vertex_storage[i].v.y);
min->z = std::min(min->z, mesh->vertex_storage[i].v.z);
max->x = std::max(max->x, mesh->vertex_storage[i].v.x);
max->y = std::max(max->y, mesh->vertex_storage[i].v.y);
max->z = std::max(max->z, mesh->vertex_storage[i].v.z);
}
}
} // namespace
void carve_getRescaleMinMax(const MeshSet<3> *left,
const MeshSet<3> *right,
Vector *min,
Vector *max)
{
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;
meshset_minmax(left, min, max);
meshset_minmax(right, min, max);
// Make sure we don't scale object with zero scale.
if (std::abs(min->x - max->x) < carve::EPSILON) {
min->x = -1.0;
max->x = 1.0;
}
if (std::abs(min->y - max->y) < carve::EPSILON) {
min->y = -1.0;
max->y = 1.0;
}
if (std::abs(min->z - max->z) < carve::EPSILON) {
min->z = -1.0;
max->z = 1.0;
}
}
namespace {
void copyMeshes(const std::vector<MeshSet<3>::mesh_t*> &meshes,
std::vector<MeshSet<3>::mesh_t*> *new_meshes)
{
std::vector<MeshSet<3>::mesh_t*>::const_iterator it = meshes.begin();
new_meshes->reserve(meshes.size());
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);
}
}
MeshSet<3> *meshSetFromMeshes(const std::vector<MeshSet<3>::mesh_t*> &meshes)
{
std::vector<MeshSet<3>::mesh_t*> new_meshes;
copyMeshes(meshes, &new_meshes);
return new MeshSet<3>(new_meshes);
}
MeshSet<3> *meshSetFromTwoMeshes(const std::vector<MeshSet<3>::mesh_t*> &left_meshes,
const std::vector<MeshSet<3>::mesh_t*> &right_meshes)
{
std::vector<MeshSet<3>::mesh_t*> new_meshes;
copyMeshes(left_meshes, &new_meshes);
copyMeshes(right_meshes, &new_meshes);
return new MeshSet<3>(new_meshes);
}
bool checkEdgeFaceIntersections_do(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;
}
bool checkEdgeFaceIntersections(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 (checkEdgeFaceIntersections_do(intersections, face_a, edge_b))
return true;
edge_b = edge_b->next;
} while (edge_b != face_b->edge);
return false;
}
inline bool 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);
}
bool checkMeshSetInterseciton_do(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 (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 (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<double, double> a_ra = fa->rangeInDirection(fa->plane.N, fa->edge->vert->v);
std::pair<double, double> b_ra = fb->rangeInDirection(fa->plane.N, fa->edge->vert->v);
if (carve::rangeSeparation(a_ra, b_ra) > carve::EPSILON) {
continue;
}
std::pair<double, double> a_rb = fa->rangeInDirection(fb->plane.N, fb->edge->vert->v);
std::pair<double, double> b_rb = fb->rangeInDirection(fb->plane.N, fb->edge->vert->v);
if (carve::rangeSeparation(a_rb, b_rb) > carve::EPSILON) {
continue;
}
if (!facesAreCoplanar(fa, fb)) {
if (checkEdgeFaceIntersections(intersections, fa, fb)) {
return true;
}
}
}
}
}
return false;
}
bool checkMeshSetInterseciton(RTreeNode<3, Face<3> *> *rtree_a, RTreeNode<3, Face<3> *> *rtree_b)
{
Intersections intersections;
return checkMeshSetInterseciton_do(intersections, rtree_a, rtree_b);
}
void getIntersectedOperandMeshes(std::vector<MeshSet<3>::mesh_t*> *meshes,
const MeshSet<3>::aabb_t &otherAABB,
std::vector<MeshSet<3>::mesh_t*> *operandMeshes,
RTreeCache *rtree_cache,
IntersectCache *intersect_cache)
{
std::vector<MeshSet<3>::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;
bool intersects;
RTreeCache::iterator rtree_found = rtree_cache->find(mesh);
if (rtree_found != rtree_cache->end()) {
rtree = rtree_found->second;
}
else {
rtree = RTreeNode<3, Face<3> *>::construct_STR(mesh->faces.begin(), mesh->faces.end(), 4, 4);
(*rtree_cache)[mesh] = rtree;
}
IntersectCache::iterator intersect_found = intersect_cache->find(mesh);
if (intersect_found != intersect_cache->end()) {
intersects = intersect_found->second;
}
else {
intersects = rtree->bbox.intersects(otherAABB);
(*intersect_cache)[mesh] = intersects;
}
if (intersects) {
bool isIntersect = false;
std::vector<MeshSet<3>::mesh_t*>::iterator operand_it = operandMeshes->begin();
std::vector<RTreeNode<3, Face<3> *> *>::iterator tree_it = meshRTree.begin();
for (; operand_it!=operandMeshes->end(); operand_it++, tree_it++) {
RTreeNode<3, Face<3> *> *operandRTree = *tree_it;
if (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<RTreeNode<3, Face<3> *> *>::iterator tree_it = meshRTree.begin();
for (; tree_it != meshRTree.end(); tree_it++) {
//delete *tree_it;
}
}
MeshSet<3> *getIntersectedOperand(std::vector<MeshSet<3>::mesh_t*> *meshes,
const MeshSet<3>::aabb_t &otherAABB,
RTreeCache *rtree_cache,
IntersectCache *intersect_cache)
{
std::vector<MeshSet<3>::mesh_t*> operandMeshes;
getIntersectedOperandMeshes(meshes, otherAABB, &operandMeshes, rtree_cache, intersect_cache);
if (operandMeshes.size() == 0)
return NULL;
return meshSetFromMeshes(operandMeshes);
}
MeshSet<3> *unionIntersectingMeshes(carve::csg::CSG *csg,
MeshSet<3> *poly,
const MeshSet<3> *other_poly,
const MeshSet<3>::aabb_t &otherAABB,
UnionIntersectionsCallback callback,
void *user_data)
{
if (poly->meshes.size() <= 1) {
return poly;
}
std::vector<MeshSet<3>::mesh_t*> orig_meshes =
std::vector<MeshSet<3>::mesh_t*>(poly->meshes.begin(), poly->meshes.end());
RTreeCache rtree_cache;
IntersectCache intersect_cache;
MeshSet<3> *left = getIntersectedOperand(&orig_meshes,
otherAABB,
&rtree_cache,
&intersect_cache);
if (!left) {
// No maniforlds which intersects another object at all.
return poly;
}
while (orig_meshes.size()) {
MeshSet<3> *right = getIntersectedOperand(&orig_meshes,
otherAABB,
&rtree_cache,
&intersect_cache);
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);
callback(result, other_poly, user_data);
delete left;
delete right;
left = result;
}
}
catch (carve::exception e) {
std::cerr << "CSG failed, exception " << e.str() << std::endl;
MeshSet<3> *result = meshSetFromTwoMeshes(left->meshes, right->meshes);
callback(result, other_poly, user_data);
delete left;
delete right;
left = result;
}
catch (...) {
delete left;
delete right;
throw "Unknown error in Carve library";
}
}
for (RTreeCache::iterator it = rtree_cache.begin();
it != rtree_cache.end();
it++)
{
delete it->second;
}
// Append all meshes which doesn't have intersection with another operand as-is.
if (orig_meshes.size()) {
MeshSet<3> *result = meshSetFromTwoMeshes(left->meshes, orig_meshes);
delete left;
left = result;
}
return left;
}
} // namespace
// TODO(sergey): This function is to be totally re-implemented to make it
// more clear what's going on and hopefully optimize it as well.
void carve_unionIntersections(carve::csg::CSG *csg,
MeshSet<3> **left_r,
MeshSet<3> **right_r,
UnionIntersectionsCallback callback,
void *user_data)
{
MeshSet<3> *left = *left_r, *right = *right_r;
if (left->meshes.size() == 1 && right->meshes.size() == 0) {
return;
}
MeshSet<3>::aabb_t leftAABB = left->getAABB();
MeshSet<3>::aabb_t rightAABB = right->getAABB();;
left = unionIntersectingMeshes(csg, left, right, rightAABB,
callback, user_data);
right = unionIntersectingMeshes(csg, right, left, leftAABB,
callback, user_data);
if (left != *left_r) {
delete *left_r;
}
if (right != *right_r) {
delete *right_r;
}
*left_r = left;
*right_r = right;
}
static inline void add_newell_cross_v3_v3v3(const Vector &v_prev,
const Vector &v_curr,
Vector *n)
{
(*n)[0] += (v_prev[1] - v_curr[1]) * (v_prev[2] + v_curr[2]);
(*n)[1] += (v_prev[2] - v_curr[2]) * (v_prev[0] + v_curr[0]);
(*n)[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]);
}
// Axis matrix is being set for non-flat ngons only.
bool carve_checkPolyPlanarAndGetNormal(const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
Matrix3 *axis_matrix_r)
{
if (verts_per_poly == 3) {
// Triangles are always planar.
return true;
}
else if (verts_per_poly == 4) {
// Presumably faster than using generig n-gon check for quads.
const Vector &v1 = vertex_storage[verts_of_poly[0]].v,
&v2 = vertex_storage[verts_of_poly[1]].v,
&v3 = vertex_storage[verts_of_poly[2]].v,
&v4 = vertex_storage[verts_of_poly[3]].v;
Vector vec1, vec2, vec3, cross;
vec1 = v2 - v1;
vec2 = v4 - v1;
vec3 = v3 - v1;
cross = carve::geom::cross(vec1, vec2);
double production = carve::geom::dot(cross, vec3);
// TODO(sergey): Check on whether we could have length-independent
// magnitude here.
double magnitude = 1e-3 * cross.length2();
return fabs(production) < magnitude;
}
else {
const Vector *vert_prev = &vertex_storage[verts_of_poly[verts_per_poly - 1]].v;
const Vector *vert_curr = &vertex_storage[verts_of_poly[0]].v;
Vector normal = carve::geom::VECTOR(0.0, 0.0, 0.0);
for (int i = 0; i < verts_per_poly; i++) {
add_newell_cross_v3_v3v3(*vert_prev, *vert_curr, &normal);
vert_prev = vert_curr;
vert_curr = &vertex_storage[verts_of_poly[(i + 1) % verts_per_poly]].v;
}
if (normal.length2() < FLT_EPSILON) {
// Degenerated face, couldn't triangulate properly anyway.
return true;
}
else {
double magnitude = normal.length2();
normal.normalize();
axis_dominant_v3_to_m3__bli(axis_matrix_r, normal);
Vector first_projected = *axis_matrix_r * vertex_storage[verts_of_poly[0]].v;
double min_z = first_projected[2], max_z = first_projected[2];
for (int i = 1; i < verts_per_poly; i++) {
const Vector &vertex = vertex_storage[verts_of_poly[i]].v;
Vector projected = *axis_matrix_r * vertex;
if (projected[2] < min_z) {
min_z = projected[2];
}
if (projected[2] > max_z) {
max_z = projected[2];
}
}
if (std::abs(min_z - max_z) > FLT_EPSILON * magnitude) {
return false;
}
}
return true;
}
return false;
}
namespace {
int triangulateNGon_carveTriangulator(const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const Matrix3 &axis_matrix,
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
std::vector<tri_idx> *triangles)
{
// Project vertices to 2D plane.
Vector projected;
std::vector<carve::geom::vector<2> > poly_2d;
poly_2d.reserve(verts_per_poly);
for (int i = 0; i < verts_per_poly; ++i) {
projected = axis_matrix * vertex_storage[verts_of_poly[i]].v;
poly_2d.push_back(carve::geom::VECTOR(projected[0], projected[1]));
}
carve::triangulate::triangulate(poly_2d, *triangles);
carve::triangulate::improve(poly_2d, *triangles);
return triangles->size();
}
int triangulateNGon_importerTriangulator(struct ImportMeshData *import_data,
CarveMeshImporter *mesh_importer,
const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const Matrix3 &axis_matrix,
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
std::vector<tri_idx> *triangles)
{
typedef float Vector2D[2];
typedef unsigned int Triangle[3];
// Project vertices to 2D plane.
Vector2D *poly_2d = new Vector2D[verts_per_poly];
Vector projected;
for (int i = 0; i < verts_per_poly; ++i) {
projected = axis_matrix * vertex_storage[verts_of_poly[i]].v;
poly_2d[i][0] = projected[0];
poly_2d[i][1] = projected[1];
}
Triangle *api_triangles = new Triangle[verts_per_poly - 2];
int num_triangles =
mesh_importer->triangulate2DPoly(import_data,
poly_2d,
verts_per_poly,
api_triangles);
triangles->reserve(num_triangles);
for (int i = 0; i < num_triangles; ++i) {
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
triangles->push_back(tri_idx(api_triangles[i][0],
api_triangles[i][1],
api_triangles[i][2]));
}
delete [] poly_2d;
delete [] api_triangles;
return num_triangles;
}
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
template <typename T>
void sortThreeNumbers(T &a, T &b, T &c)
{
if (a > b)
std::swap(a, b);
if (b > c)
std::swap(b, c);
if (a > b)
std::swap(a, b);
}
bool pushTriangle(int v1, int v2, int v3,
std::vector<int> *face_indices,
TrianglesStorage *triangles_storage)
{
tri_idx triangle(v1, v2, v3);
sortThreeNumbers(triangle.a, triangle.b, triangle.c);
assert(triangle.a < triangle.b);
assert(triangle.b < triangle.c);
if (triangles_storage->find(triangle) == triangles_storage->end()) {
face_indices->push_back(v1);
face_indices->push_back(v2);
face_indices->push_back(v3);
triangles_storage->insert(triangle);
return true;
}
else {
return false;
}
}
} // namespace
int carve_triangulatePoly(struct ImportMeshData *import_data,
CarveMeshImporter *mesh_importer,
const std::vector<MeshSet<3>::vertex_t> &vertex_storage,
const int verts_per_poly,
const int *verts_of_poly,
const Matrix3 &axis_matrix,
std::vector<int> *face_indices,
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
TrianglesStorage *triangles_storage)
{
int num_triangles = 0;
assert(verts_per_poly > 3);
if (verts_per_poly == 4) {
// Quads we triangulate by 1-3 diagonal, it is an original behavior
// of boolean modifier.
//
// TODO(sergey): Consider using shortest diagonal here. However
// display code in Blende use static 1-3 split, so some experiments
// are needed here.
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
if (pushTriangle(verts_of_poly[0],
verts_of_poly[1],
verts_of_poly[2],
face_indices,
triangles_storage))
{
num_triangles++;
}
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
if (pushTriangle(verts_of_poly[0],
verts_of_poly[2],
verts_of_poly[3],
face_indices,
triangles_storage))
{
num_triangles++;
}
}
else {
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
std::vector<tri_idx> triangles;
triangles.reserve(verts_per_poly - 2);
// Make triangulator callback optional so we could do some tests
// in the future.
if (mesh_importer->triangulate2DPoly) {
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
triangulateNGon_importerTriangulator(import_data,
mesh_importer,
vertex_storage,
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
verts_per_poly,
verts_of_poly,
axis_matrix,
&triangles);
}
else {
triangulateNGon_carveTriangulator(vertex_storage,
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
verts_per_poly,
verts_of_poly,
axis_matrix,
&triangles);
}
for (int i = 0; i < triangles.size(); ++i) {
int v1 = triangles[i].a,
v2 = triangles[i].b,
v3 = triangles[i].c;
// Sanity check of the triangle.
assert(v1 != v2);
assert(v1 != v3);
assert(v2 != v3);
assert(v1 < verts_per_poly);
assert(v2 < verts_per_poly);
assert(v3 < verts_per_poly);
Fix T38918: Boolean modifier crashes when using specific topology There were loads of issues in the code still which are mow likely fixed: - Hole resolver hook had memory leak -- it didn't free face with holes when triangulating it. - Original edge mapping didn't work correct. old code related on the fact that loop order is not changing when constructing the MeshSet class, but in fact it does change. Currently used edge map for this because it was easiest way to do it now, but after the release we're to change it. Main reason is that face mapping is not correct as well (and it was never correct actually). So we'll need to construct Mesh structures by our own to be sure we're using correct original index mapping. - Carve might produce faces with ears, which is forbidden in Blender. it wasn't an issue in old integration because triangulation will remove the ears. So for now simply added ears removing back as a hook. But actual reason of the ears is to be investigated really. This hook will only work for NGons, quads are assumed not be able to have ears. So this additional hook shouldn't slow down things much. - Carve's hole resolver produces duplicated faces in some cases. Still not sure what is the reason of this. Code here is not so much straightforward, this is to be investigated later. For now solved the issue as own hole resolver which checks for duplicated faces after the hole resolving. The additional checks here will only run if the mesh actually have hole and wouldn't introduce slowdown for faces which doesn't have holes. - Made it so if edge user triangulation gets a split (for example, in cases when this edge intersects with the second operand) it wouldn't be dissolved. This prevents cases of crappy topology after dissolving in several cases. - Edge dissolver didn't check for whether edge is a non-manifold. We couldn't really dissolve open manifold edges. The bad thing about this is that mesh triangulation might produce non-manifold edges and they wouldn't be dissolved. Not worst case in the world, but would be nice to have it solved somehow. - Exporting mesh form Carve to Blender might have produced duplicated edges in cases when several non-manifold faces shared the edge. This is also fixed now. - Mesh triangulation might have produced duplicated faces, which is really bad. Fixed by keeping a track on which faces we've created and skipping adding new triangle if we already have one. This all might introduce some slowdown, but we're too close to the release now, so would rather have it slower but robust. After the release we might look into ways to speed things up.
2014-03-04 14:01:58 +00:00
if (pushTriangle(verts_of_poly[v1],
verts_of_poly[v2],
verts_of_poly[v3],
face_indices,
triangles_storage))
{
num_triangles++;
}
}
}
return num_triangles;
}