Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Graphics/csg.cpp')
-rw-r--r--Source/Graphics/csg.cpp1320
1 files changed, 1320 insertions, 0 deletions
diff --git a/Source/Graphics/csg.cpp b/Source/Graphics/csg.cpp
new file mode 100644
index 00000000..932d894e
--- /dev/null
+++ b/Source/Graphics/csg.cpp
@@ -0,0 +1,1320 @@
+//-----------------------------------------------------------------------------
+// Name: csg.cpp
+// Developer: Wolfire Games LLC
+// Description:
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+#include "csg.h"
+
+#include <Graphics/model.h>
+#include <Graphics/models.h>
+#include <Graphics/pxdebugdraw.h>
+
+#include <Math/enginemath.h>
+#include <Math/triangle.h>
+#include <Math/vec2math.h>
+
+#include <Physics/bulletworld.h>
+#include <Memory/allocation.h>
+#include <Logging/logdata.h>
+#include <Utility/compiler_macros.h>
+
+#include <opengl.h>
+
+#include <algorithm>
+#include <vector>
+
+namespace Test {
+ struct HalfEdge {
+ int points[2];
+ HalfEdge *twin, *next;
+ };
+
+ struct HalfEdgeSort {
+ HalfEdge *edge;
+ int id;
+ };
+
+ bool operator<(const HalfEdgeSort& a, const HalfEdgeSort& b) {
+ int temp_points[2][2];
+ temp_points[0][0] = a.edge->points[0];
+ temp_points[0][1] = a.edge->points[1];
+ temp_points[1][0] = b.edge->points[0];
+ temp_points[1][1] = b.edge->points[1];
+ for(int i=0; i<2; ++i){
+ if(temp_points[i][0] > temp_points[i][1]){
+ std::swap(temp_points[i][0], temp_points[i][1]);
+ }
+ }
+ if(temp_points[0][0] == temp_points[1][0]){
+ return temp_points[0][1] < temp_points[1][1];
+ } else {
+ return temp_points[0][0] < temp_points[1][0];
+ }
+ }
+
+ struct Edge {
+ int points[2];
+ };
+
+ bool operator<(const Edge& a, const Edge& b){
+ if(a.points[0] == b.points[0]){
+ return a.points[1] < b.points[1];
+ } else {
+ return a.points[0] < b.points[0];
+ }
+ }
+
+ bool operator!=(const Edge& a, const Edge& b){
+ return (a.points[0] != b.points[0] || a.points[1] != b.points[1]);
+ }
+}
+
+namespace {
+ /*
+ float orient2d(const float *a, const float *b, const float *c) {
+ return (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]);
+ }
+ */
+
+ double orient2d_double(const double *a, const double *b, const double *c) {
+ return (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]);
+ }
+
+ struct TriangleEdge {
+ int points[2];
+ int tri;
+ };
+
+ /*
+ bool operator<(const TriangleEdge& a, const TriangleEdge &b){
+ if(a.points[0] == b.points[0]){
+ return a.points[1] < b.points[1];
+ } else {
+ return a.points[0] < b.points[0];
+ }
+ }
+ */
+
+ bool NeighborSort(const TriangleEdge& a, const TriangleEdge &b){
+ int points[2][2];
+ for(int i=0; i<2; ++i){
+ points[0][i] = a.points[i];
+ points[1][i] = b.points[i];
+ }
+ for(int i=0; i<2; ++i){
+ if(points[i][0] > points[i][1]){
+ std::swap(points[i][0], points[i][1]);
+ }
+ }
+ if(points[0][0] == points[1][0]){
+ return points[0][1] < points[1][1];
+ } else {
+ return points[0][0] < points[1][0];
+ }
+ }
+
+ typedef std::pair<int, int> TriNeighbor;
+
+ /*
+ bool operator<(const TriNeighbor& a, const TriNeighbor& b){
+ return a.first < b.first;
+ }
+ */
+
+ struct TriNeighborInfo {
+ std::vector<TriNeighbor> tri_neighbors;
+ std::vector<int> num_tri_neighbors;
+ std::vector<int> tri_neighbor_index;
+ };
+
+ void GetTriNeighbors(const std::vector<int> &faces, const std::vector<double> &verts, TriNeighborInfo *info, bool display){
+ info->tri_neighbors.clear();
+ info->num_tri_neighbors.clear();
+ info->tri_neighbor_index.clear();
+ std::vector<TriangleEdge> edges;
+ edges.reserve(faces.size());
+ for(int i=0, len=faces.size(); i<len; i+=3){
+ for(int j=0; j<3; ++j){
+ TriangleEdge edge;
+ edge.points[0] = faces[i+j];
+ edge.points[1] = faces[i+(j+1)%3];
+ edge.tri = i/3;
+ edges.push_back(edge);
+ }
+ }
+ std::sort(edges.begin(), edges.end(), NeighborSort);
+ std::vector<int> edge_twin(edges.size(), -1);
+ for(int i=1, len=edges.size(); i<len; ++i){
+ if(edges[i].points[0] == edges[i-1].points[1] && edges[i].points[1] == edges[i-1].points[0]){
+ edge_twin[i] = i-1;
+ edge_twin[i-1] = i;
+ }
+ }
+ for(int i=0, len=edge_twin.size(); i<len; ++i){
+ if(edge_twin[i] != -1 && edge_twin[edge_twin[i]] == i){
+ TriNeighbor neighbor;
+ neighbor.first = edges[i].tri;
+ neighbor.second = edges[edge_twin[i]].tri;
+ info->tri_neighbors.push_back(neighbor);
+ } else {
+ if(display){
+ vec3 points[2];
+ TriangleEdge &edge = edges[i];
+ for(int j=0; j<2; ++j){
+ int vert_index = edge.points[j]*3;
+ for(int i=0; i<3; ++i){
+ points[j][i] = (float)verts[vert_index+i];
+ }
+ }
+ DebugDraw::Instance()->AddLine(points[0], points[1], vec4(1.0f), _persistent, _DD_XRAY);
+ }
+ }
+ }
+ std::sort(info->tri_neighbors.begin(), info->tri_neighbors.end());
+ info->num_tri_neighbors.resize(faces.size()/3, 0);
+ info->tri_neighbor_index.resize(faces.size()/3, -1);
+ for(int i=0, len=info->tri_neighbors.size(); i<len; ++i){
+ int tri = info->tri_neighbors[i].first;
+ if(info->tri_neighbor_index[tri] == -1){
+ info->tri_neighbor_index[tri] = i;
+ }
+ ++info->num_tri_neighbors[tri];
+ }
+ }
+
+ struct vec3d {
+ double entries[3];
+ vec3d(double val){
+ for(int i=0; i<3; ++i){
+ entries[i] = val;
+ }
+ }
+ vec3d(){
+ for(int i=0; i<3; ++i){
+ entries[i] = 0.0;
+ }
+ }
+ double& operator[](int which){
+ return entries[which];
+ }
+ const double& operator[](int which) const {
+ return entries[which];
+ }
+ };
+
+ struct TriIntersect {
+ std::pair<int, int> edge_id[2];
+ vec3d true_intersect[2];
+ bool valid;
+ };
+
+ struct TriIntersectInfo {
+ int tris[2];
+ TriIntersect tri_intersect;
+ };
+
+ bool TriIntersectInfoSortFirstTri(const TriIntersectInfo& a, const TriIntersectInfo& b) {
+ return a.tris[0] < b.tris[0];
+ }
+
+ bool TriIntersectInfoSortSecondTri(const TriIntersectInfo& a, const TriIntersectInfo& b) {
+ return a.tris[1] < b.tris[1];
+ }
+
+ vec3d operator-(const vec3d &a, const vec3d &b){
+ vec3d result;
+ for(int i=0; i<3; ++i){
+ result[i] = a[i] - b[i];
+ }
+ return result;
+ }
+
+ vec3d operator+(const vec3d &a, const vec3d &b){
+ vec3d result;
+ for(int i=0; i<3; ++i){
+ result[i] = a[i] + b[i];
+ }
+ return result;
+ }
+
+ vec3d operator*(const vec3d &a, double b){
+ vec3d result;
+ for(int i=0; i<3; ++i){
+ result[i] = a[i] * b;
+ }
+ return result;
+ }
+
+ vec3d cross(const vec3d &vec_a, const vec3d &vec_b) {
+ vec3d result;
+ result[0] = vec_a[1] * vec_b[2] - vec_a[2] * vec_b[1];
+ result[1] = vec_a[2] * vec_b[0] - vec_a[0] * vec_b[2];
+ result[2] = vec_a[0] * vec_b[1] - vec_a[1] * vec_b[0];
+ return result;
+ }
+
+ double length_squared(const vec3d &vec) {
+ return vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2];
+ }
+
+ vec3d normalize(const vec3d &vec) {
+ double length_squared_val = length_squared(vec);
+ if(length_squared_val == 0.0){
+ return vec3d(0.0);
+ }
+ return vec*(1.0/sqrt(length_squared_val));
+ }
+
+ double dot(const vec3d &vec_a, const vec3d &vec_b) {
+ return vec_a[0]*vec_b[0] + vec_a[1]*vec_b[1] + vec_a[2]*vec_b[2];
+ }
+
+ void GetTriIntersection(const vec3d tri[][3], TriIntersect &tri_intersect){
+ double tri_intersect_t[2][2];
+ int tri_intersect_outlier[2];
+ // Get normal for each triangle
+ vec3d norm[2];
+ for(int i=0; i<2; ++i){
+ norm[i] = normalize(cross(tri[i][1] - tri[i][0], tri[i][2] - tri[i][0]));
+ }
+ // Find position of each triangle along its normal
+ double plane_d[2];
+ for(int i=0; i<2; ++i){
+ plane_d[i] = dot(tri[i][0], norm[i]);
+ }
+ // Get position of each triangle vertex relative to the plane of the other triangle
+ double other_plane_d[2][3];
+ for(int i=0; i<2; ++i){
+ for(int j=0; j<3; ++j){
+ other_plane_d[i][j] = dot(tri[i][j], norm[1-i]) - plane_d[1-i];
+ }
+ }
+ // Get intersection points of each triangle with the other triangle's plane
+ for(int i=0; i<2; ++i){
+ // Get number of vertices on each side of other triangle's plane
+ int num_pos = 0;
+ int num_neg = 0;
+ for(int j=0; j<3; ++j){
+ if(other_plane_d[i][j] > 0.0){
+ ++num_pos;
+ } else {
+ ++num_neg;
+ }
+ }
+ if(num_pos == 3 || num_neg == 3){
+ // All vertices of triangle are on the same side of the other triangle
+ // No intersection!
+ tri_intersect.valid = false;
+ return;
+ }
+ // Get the vertex that is on the opposite side of the plane from the other vertices
+ int outlier = -1;
+ tri_intersect_outlier[i] = -1;
+ if(num_pos == 1){
+ for(int j=0; j<3; ++j){
+ if(other_plane_d[i][j] > 0.0){
+ outlier = j;
+ }
+ }
+ } else {
+ for(int j=0; j<3; ++j){
+ if(other_plane_d[i][j] <= 0.0){
+ outlier = j;
+ }
+ }
+ }
+ // Get intersection points
+ double t[2];
+ t[0] = other_plane_d[i][outlier] / (other_plane_d[i][outlier] - other_plane_d[i][(outlier+1)%3]);
+ t[1] = other_plane_d[i][outlier] / (other_plane_d[i][outlier] - other_plane_d[i][(outlier+2)%3]);
+ tri_intersect_outlier[i] = outlier;
+ tri_intersect_t[i][0] = t[0];
+ tri_intersect_t[i][1] = t[1];
+ }
+ vec3d intersect[2][2];
+ for(int i=0; i<2; ++i){
+ intersect[i][0] = mix(tri[i][tri_intersect_outlier[i]], tri[i][(tri_intersect_outlier[i]+1)%3], tri_intersect_t[i][0]);
+ intersect[i][1] = mix(tri[i][tri_intersect_outlier[i]], tri[i][(tri_intersect_outlier[i]+2)%3], tri_intersect_t[i][1]);
+ }
+ // Find the intersection of the two segments
+ vec3d intersect_dir = normalize(intersect[0][1] - intersect[0][0]);
+ int t_edge[2][2]; // Is this edge outlier+1 or outlier+2?
+ double t[2][2];
+ for(int i=0; i<2; ++i){
+ for(int j=0; j<2; ++j){
+ t[i][j] = dot(intersect[i][j] - intersect[0][0], intersect_dir);
+ t_edge[i][j] = j;
+ }
+ if(t[i][0] > t[i][1]){
+ std::swap(t[i][0], t[i][1]);
+ std::swap(t_edge[i][0], t_edge[i][1]);
+ }
+ }
+ if(t[0][1] < t[1][0] || t[1][1] < t[0][0]){
+ // The intersection segments do not overlap
+ // Therefore the triangles do not actually intersect
+ tri_intersect.valid = false;
+ return;
+ }
+ double intersect_bounds[2];
+ if(t[0][0] > t[1][0]){
+ tri_intersect.edge_id[0].first = 0;
+ tri_intersect.edge_id[0].second = t_edge[0][0];
+ intersect_bounds[0] = t[0][0];
+ } else {
+ tri_intersect.edge_id[0].first = 1;
+ tri_intersect.edge_id[0].second = t_edge[1][0];
+ intersect_bounds[0] = t[1][0];
+ }
+ if(t[0][1] < t[1][1]){
+ tri_intersect.edge_id[1].first = 0;
+ tri_intersect.edge_id[1].second = t_edge[0][1];
+ intersect_bounds[1] = t[0][1];
+ } else {
+ tri_intersect.edge_id[1].first = 1;
+ tri_intersect.edge_id[1].second = t_edge[1][1];
+ intersect_bounds[1] = t[1][1];
+ }
+ tri_intersect.true_intersect[0] = intersect_dir * (float)intersect_bounds[0] + intersect[0][0];
+ tri_intersect.true_intersect[1] = intersect_dir * (float)intersect_bounds[1] + intersect[0][0];
+ tri_intersect.valid = true;
+ }
+
+ typedef std::pair<double, int> EdgeAngleSort;
+
+ /*
+ bool operator<(const EdgeAngleSort &a, const EdgeAngleSort &b) {
+ return a.first < b.first;
+ }
+ */
+
+
+ void TriangulateWrapper(std::vector<float> &points_in, std::vector<int> &edges, std::vector<GLuint> &faces_out) {
+ struct triangulateio in;
+ struct triangulateio out;
+
+ in.numberofpoints = (int)points_in.size()/2;
+ in.numberofpointattributes = 1;
+ in.pointmarkerlist = 0;
+ std::vector<tREAL> in_points(in.numberofpoints*2);
+ for(int i=0, len=points_in.size(); i<len; ++i){
+ in_points[i] = points_in[i];
+ }
+ in.pointlist = &in_points.front();
+ std::vector<tREAL> in_point_attributes(in.numberofpoints);
+ for (int i=0; i<in.numberofpoints; i++){
+ in_point_attributes[i] = i;
+ }
+ in.pointattributelist = &in_point_attributes.front();
+
+ std::vector<int> new_edges = edges;
+ in.numberofsegments = new_edges.size()/2;
+ in.segmentmarkerlist = NULL;
+ if(!new_edges.empty()){
+ in.segmentlist = &new_edges.front();
+ } else {
+ in.segmentlist = NULL;
+ }
+
+ in.numberofholes = 0;
+ in.holelist = NULL;
+ in.numberofregions = 0;
+ in.regionlist = NULL;
+
+ out.pointlist = NULL;
+ out.pointattributelist = NULL;
+ out.trianglelist = NULL;
+ out.segmentlist = NULL;
+ out.segmentmarkerlist = NULL;
+ out.pointmarkerlist = NULL;
+
+ triangulate("zQ", &in, &out, 0);
+
+ faces_out.resize(out.numberoftriangles*3);
+
+ int face_index = 0;
+ for (int i = 0; i<out.numberoftriangles; i++){
+ faces_out[face_index++]=(GLuint)(out.pointattributelist[out.trianglelist[i*3+2]]);
+ faces_out[face_index++]=(GLuint)(out.pointattributelist[out.trianglelist[i*3+1]]);
+ faces_out[face_index++]=(GLuint)(out.pointattributelist[out.trianglelist[i*3+0]]);
+ }
+
+ OG_FREE(out.pointlist);
+ OG_FREE(out.trianglelist);
+ OG_FREE(out.segmentlist);
+ OG_FREE(out.segmentmarkerlist);
+ OG_FREE(out.pointmarkerlist);
+ OG_FREE(out.pointattributelist);
+ }
+
+ struct vec2d {
+ double entries[2];
+ double& operator[](int which){
+ return entries[which];
+ }
+ const double& operator[](int which) const{
+ return entries[which];
+ }
+ };
+
+ vec2d operator-(const vec2d &a, const vec2d &b){
+ vec2d result;
+ for(int i=0; i<2; ++i){
+ result[i] = a[i] - b[i];
+ }
+ return result;
+ }
+
+ vec2d operator+(const vec2d &a, const vec2d &b){
+ vec2d result;
+ for(int i=0; i<2; ++i){
+ result[i] = a[i] + b[i];
+ }
+ return result;
+ }
+
+ vec2d operator*(const vec2d &a, double b){
+ vec2d result;
+ for(int i=0; i<2; ++i){
+ result[i] = a[i] * b;
+ }
+ return result;
+ }
+
+ double length_squared(const vec2d &a){
+ return a[0]*a[0] + a[1]*a[1];
+ }
+
+ double distance_squared(const vec2d &a, const vec2d &b){
+ vec2d diff = b-a;
+ return length_squared(diff);
+ }
+
+ struct TrianglePoint {
+ vec2d flat_coord;
+ vec3d coord;
+ vec3d norm;
+ vec3d bary;
+ int id;
+ bool on_edge[3];
+ enum Side {
+ OUTSIDE, EDGE, INSIDE, UNKNOWN
+ };
+ Side side;
+ };
+
+ bool operator<(const TrianglePoint& a, const TrianglePoint& b){
+ if(a.flat_coord[0] == b.flat_coord[0]){
+ return a.flat_coord[1] < b.flat_coord[1];
+ } else {
+ return a.flat_coord[0] < b.flat_coord[0];
+ }
+ }
+
+ struct TriIntersectionShape {
+ ShapeDisposalData sdd;
+ std::vector<double> vertices;
+ std::vector<float> vertices_float;
+ std::vector<int> faces;
+ btGImpactMeshShape *gimpact_shape;
+ btCollisionObject col_obj;
+ };
+
+ struct VertSorter {
+ double coord[3];
+ int old_id;
+ int merge_target;
+ };
+
+ bool VertSortByCoord(const VertSorter &a, const VertSorter &b){
+ if(a.coord[0] == b.coord[0]){
+ if(a.coord[1] == b.coord[1]){
+ return a.coord[2] < b.coord[2];
+ }
+ return a.coord[1] < b.coord[1];
+ }
+ return a.coord[0] < b.coord[0];
+ }
+
+ bool VertSortById(const VertSorter &a, const VertSorter &b){
+ return a.old_id < b.old_id;
+ }
+
+ bool vec3d_equal(const double *a, const double *b){
+ return (a[0] == b[0] && a[1] == b[1] && a[2] == b[2]);
+ }
+
+ void MergeDoubleVerts(const std::vector<double>& vertices, std::vector<int>& faces, bool remove_degenerate_faces){
+ // Merge overlapping vertices
+ // -- sort vertices by coordinate
+ std::vector<VertSorter> vert_sort;
+ vert_sort.resize(vertices.size()/3);
+ for(int i=0, vert=0, len=vertices.size(); i<len; i+=3, ++vert){
+ for(int j=0; j<3; ++j){
+ vert_sort[vert].coord[j] = vertices[i+j];
+ }
+ vert_sort[vert].old_id = vert;
+ vert_sort[vert].merge_target = vert;
+ }
+ std::sort(vert_sort.begin(), vert_sort.end(), VertSortByCoord);
+ // -- get merge target for each vert
+ for(int i=1, len=vert_sort.size(); i<len; ++i){
+ if(vec3d_equal(vert_sort[i-1].coord, vert_sort[i].coord)){
+ vert_sort[i].merge_target = vert_sort[i-1].merge_target;
+ }
+ }
+ std::sort(vert_sort.begin(), vert_sort.end(), VertSortById);
+ // -- set faces to use merge targets
+ for(int i=0, len=faces.size(); i<len; ++i){
+ faces[i] = vert_sort[faces[i]].merge_target;
+ }
+ if(remove_degenerate_faces){
+ // -- remove degenerate faces
+ for(int i=faces.size()-3; i>=0; i-=3){
+ bool degenerate = false;
+ for(int j=0; j<3; ++j){
+ if(faces[i+j] == faces[i+(j+1)%3]){
+ degenerate = true;
+ }
+ }
+ if(degenerate){
+ for(int j=0; j<3; ++j){
+ faces[i+j] = faces[faces.size()-3+j];
+ }
+ faces.resize(faces.size()-3);
+ }
+ }
+ }
+ }
+
+ void FillTriIntersectionShape(const Model& model, const mat4& transform, TriIntersectionShape* shape){
+ // Transform vertices
+ for(int i=0, len=model.vertices.size(); i<len; i += 3){
+ double vec[3];
+ vec[0] = (double)model.vertices[i+0];
+ vec[1] = (double)model.vertices[i+1];
+ vec[2] = (double)model.vertices[i+2];
+ MultiplyMat4Vec3D(transform, vec);
+ shape->vertices.push_back(vec[0]);
+ shape->vertices.push_back(vec[1]);
+ shape->vertices.push_back(vec[2]);
+ }
+ shape->faces.reserve(model.faces.size());
+ for(int i=0, len=model.faces.size(); i<len; ++i){
+ shape->faces.push_back(model.faces[i]);
+ }
+ // Create Bullet collision shape
+ shape->vertices_float.reserve(shape->vertices.size());
+ for(int i=0, len=shape->vertices.size(); i<len; ++i){
+ shape->vertices_float.push_back((float)shape->vertices[i]);
+ }
+ shape->gimpact_shape = BulletWorld::CreateDynamicMeshShape(shape->faces, shape->vertices_float, shape->sdd);
+ shape->col_obj.setCollisionShape((btCollisionShape*)shape->gimpact_shape);
+ }
+
+ struct LabeledEdge {
+ int points[3];
+ TrianglePoint::Side side;
+ vec3d norm;
+ int id;
+ int tri;
+ };
+
+ bool operator<(const LabeledEdge& a, const LabeledEdge& b){
+ if(a.points[0] == b.points[0]){
+ return a.points[1] < b.points[1];
+ } else {
+ return a.points[0] < b.points[0];
+ }
+ }
+
+ struct TriIntersectOutput {
+ std::vector<int> *faces_to_remove;
+ std::vector<double> *new_bary;
+ std::vector<double> *new_vertices;
+ std::vector<int> *new_vert_tri_points;
+ std::vector<int> *new_faces;
+ std::vector<TrianglePoint::Side> *new_triangle_sides;
+ };
+
+ void GetNewTris(int which, std::vector<TriIntersectInfo> &tri_intersect_info, TriIntersectionShape shape[], TriIntersectOutput &output) {
+ // sort triangle intersections based on which object we are dealing with
+ if(which == 0){
+ std::sort(tri_intersect_info.begin(), tri_intersect_info.end(), TriIntersectInfoSortFirstTri);
+ } else {
+ std::sort(tri_intersect_info.begin(), tri_intersect_info.end(), TriIntersectInfoSortSecondTri);
+ }
+ // Handle one triangle at a time
+ int old_index = 0;
+ for(int new_index=1, len=tri_intersect_info.size(); new_index<=len; ++new_index){
+ if(new_index==len || tri_intersect_info[new_index].tris[which] != tri_intersect_info[old_index].tris[which]){
+ std::vector<LabeledEdge> labeled_edges;
+ // Look at tri_intersect_info old_index - i to get intersecting edges
+ int tri_index = tri_intersect_info[old_index].tris[which]*3;
+ output.faces_to_remove->push_back(tri_intersect_info[old_index].tris[which]);
+ vec3d tri_vert[3];
+ int tri_vert_id[3];
+ for(int j=0; j<3; ++j){
+ tri_vert_id[j] = shape[1-which].faces[tri_index+j];
+ int vert_index = tri_vert_id[j]*3;
+ for(int k=0; k<3; ++k){
+ tri_vert[j][k] = shape[1-which].vertices[vert_index+k];
+ }
+ }
+ // Create orthonormal basis on plane of triangle
+ vec3d norm = normalize(cross(tri_vert[1]-tri_vert[0], tri_vert[2]-tri_vert[0]));
+ vec3d basis[2];
+ basis[0] = normalize(tri_vert[1] - tri_vert[0]);
+ basis[1] = tri_vert[2] - tri_vert[0];
+ basis[1] = normalize(basis[1] - basis[0] * dot(basis[0], basis[1]));
+ // Add initial points to set
+ std::vector<TrianglePoint> triangle_points;
+ std::vector<int> on_edge;
+ for(int j=0; j<3; ++j){
+ on_edge.push_back(triangle_points.size());
+ TrianglePoint point;
+ point.bary = vec3d(0.0);
+ point.bary[j] = 1.0;
+ point.coord = tri_vert[j];
+ point.flat_coord[0] = dot(basis[0], tri_vert[j]);
+ point.flat_coord[1] = dot(basis[1], tri_vert[j]);
+ point.id = triangle_points.size();
+ point.on_edge[j] = true;
+ point.on_edge[(j+2)%3] = true;
+ point.on_edge[(j+1)%3] = false;
+ point.side = TrianglePoint::UNKNOWN;
+ triangle_points.push_back(point);
+ }
+ std::vector<int> edges;
+ // Loop through triangle intersections
+ for(int j=old_index; j<new_index; ++j){
+ TriIntersect &tri_intersect = tri_intersect_info[j].tri_intersect;
+ // Add triangle point for each intersection vertex
+ for(int k=0; k<2; ++k){
+ TrianglePoint point;
+ point.coord = tri_intersect.true_intersect[k];
+ point.flat_coord[0] = dot(basis[0], tri_intersect.true_intersect[k]);
+ point.flat_coord[1] = dot(basis[1], tri_intersect.true_intersect[k]);
+ double vec_a[2], vec_b[2];
+ for(int l=0; l<2; ++l){
+ vec_a[l] = triangle_points[1].flat_coord[l] - triangle_points[0].flat_coord[l];
+ vec_b[l] = triangle_points[2].flat_coord[l] - triangle_points[0].flat_coord[l];
+ }
+ double twice_area = vec_a[0] * vec_b[1] - vec_a[1] * vec_b[0];
+ point.bary[0] = orient2d_double(triangle_points[1].flat_coord.entries, triangle_points[2].flat_coord.entries, point.flat_coord.entries);
+ point.bary[1] = orient2d_double(triangle_points[2].flat_coord.entries, triangle_points[0].flat_coord.entries, point.flat_coord.entries);
+ point.bary[2] = orient2d_double(triangle_points[0].flat_coord.entries, triangle_points[1].flat_coord.entries, point.flat_coord.entries);
+ point.bary[0] /= twice_area;
+ point.bary[1] /= twice_area;
+ point.bary[2] /= twice_area;
+ point.id = triangle_points.size();
+ point.on_edge[0] = false;
+ point.on_edge[1] = false;
+ point.on_edge[2] = false;
+ point.side = TrianglePoint::EDGE;
+ if(tri_intersect.edge_id[k].first == which){
+ on_edge.push_back(triangle_points.size());
+ }
+ triangle_points.push_back(point);
+ }
+ // Add edge for triangulation
+ edges.push_back(triangle_points.size()-2);
+ edges.push_back(triangle_points.size()-1);
+ // Add labeled_edge for processing later
+ LabeledEdge labeled_edge;
+ labeled_edge.points[0] = triangle_points.size()-2;
+ labeled_edge.points[1] = triangle_points.size()-1;
+ labeled_edge.points[2] = -1;
+ if(labeled_edge.points[0] > labeled_edge.points[1]){
+ std::swap(labeled_edge.points[0], labeled_edge.points[1]);
+ }
+ labeled_edge.side = TrianglePoint::EDGE;
+ // Get vertices of intersected triangle
+ vec3d other_tri_vert[3];
+ int other_tri_index = tri_intersect_info[j].tris[1-which]*3;
+ for(int j=0; j<3; ++j){
+ int vert_index = shape[which].faces[other_tri_index+j]*3;
+ for(int k=0; k<3; ++k){
+ other_tri_vert[j][k] = shape[which].vertices[vert_index+k];
+ }
+ }
+ // Label the edge with the normal of the other triangle
+ labeled_edge.norm = normalize(cross(other_tri_vert[1] - other_tri_vert[0], other_tri_vert[2] - other_tri_vert[0]));
+ labeled_edges.push_back(labeled_edge);
+ }
+ // Calculate the angle from the center of the tri to each edge tri
+ std::vector<EdgeAngleSort> edge_angle;
+ edge_angle.reserve(on_edge.size());
+ vec2d mid_point;
+ mid_point = (triangle_points[0].flat_coord + triangle_points[1].flat_coord + triangle_points[2].flat_coord) * (1.0 / 3.0);
+ for(int j=0, len=on_edge.size(); j<len; ++j){
+ EdgeAngleSort eas;
+ eas.second = on_edge[j];
+ vec2d offset = triangle_points[on_edge[j]].flat_coord - mid_point;
+ eas.first = atan2(offset[1], offset[0]);
+ edge_angle.push_back(eas);
+ }
+ // Add edges sorted by angle
+ std::sort(edge_angle.begin(), edge_angle.end());
+ for(int j=0, len=edge_angle.size(); j<len; ++j){
+ edges.push_back(edge_angle[j].second);
+ edges.push_back(edge_angle[(j+1)%len].second);
+ }
+ // Get positions of each triangle corner on edge loop
+ int edge_pos[3];
+ for(int j=0, len=edge_angle.size(); j<len; ++j){
+ for(int k=0; k<3; ++k){
+ if(edge_angle[j].second == k){
+ edge_pos[k] = j;
+ }
+ }
+ }
+ // Mark which edge each non-corner edge vertex is on
+ for(int k=0; k<3; ++k){
+ for(int j=edge_pos[k]+1;; ++j){
+ if(j >= (int)edge_angle.size()){
+ j = 0;
+ }
+ if(j == edge_pos[(k+1)%3]){
+ break;
+ }
+ triangle_points[edge_angle[j].second].on_edge[k] = true;
+ }
+ }
+ // Remove duplicate points
+ std::vector<int> triangle_point_remap(triangle_points.size(), 0);
+ std::sort(triangle_points.begin(), triangle_points.end());
+ if(!triangle_points.empty()){
+ triangle_point_remap[triangle_points[0].id] = 0;
+ int index=0;
+ for(int i=1, len=triangle_points.size(); i<len; ++i){
+ if(distance_squared(triangle_points[i].flat_coord, triangle_points[i-1].flat_coord) > 0.0000001){
+ ++index;
+ triangle_point_remap[triangle_points[i].id] = index;
+ triangle_points[index] = triangle_points[i];
+ } else {
+ triangle_point_remap[triangle_points[i].id] = index;
+ }
+ }
+ triangle_points.resize(index+1);
+ }
+ // Remap edges to new points
+ for(int i=0, len=edges.size(); i<len; ++i){
+ edges[i] = triangle_point_remap[edges[i]];
+ }
+ for(int i=0, len=labeled_edges.size(); i<len; ++i){
+ labeled_edges[i].points[0] = triangle_point_remap[labeled_edges[i].points[0]];
+ labeled_edges[i].points[1] = triangle_point_remap[labeled_edges[i].points[1]];
+ if(labeled_edges[i].points[0] > labeled_edges[i].points[1]){
+ std::swap(labeled_edges[i].points[0], labeled_edges[i].points[1]);
+ }
+ }
+ // Get points to input to triangulation
+ std::vector<float> points;
+ for(int i=0, len=triangle_points.size(); i<len; ++i){
+ for(int j=0; j<2; ++j){
+ points.push_back((float)triangle_points[i].flat_coord[j]);
+ }
+ }
+ // Triangulate
+ std::vector<GLuint> faces;
+ TriangulateWrapper(points, edges, faces);
+ // Add new vertices
+ int vert_start = output.new_bary->size()/3;
+ for(int j=0, len=triangle_points.size(); j<len; ++j){
+ for(int k=0; k<3; ++k){
+ output.new_vertices->push_back(triangle_points[j].coord[k]);
+ output.new_bary->push_back(triangle_points[j].bary[k]);
+ output.new_vert_tri_points->push_back(tri_vert_id[k]);
+ }
+ }
+ // Remove triangles that are all on same edge
+ int face_index = 0;
+ for(int j=0, len=faces.size(); j<len; j+=3){
+ bool includes_bad_vert = false;
+ for(int k=0; k<3; ++k){
+ if(triangle_points[faces[j+0]].on_edge[k] &&
+ triangle_points[faces[j+1]].on_edge[k] &&
+ triangle_points[faces[j+2]].on_edge[k])
+ {
+ includes_bad_vert = true;
+ }
+ }
+ if(!includes_bad_vert){
+ for(int k=0; k<3; ++k){
+ faces[face_index+k] = faces[j+k];
+ }
+ face_index += 3;
+ }
+ }
+ faces.resize(face_index);
+ // Make sure faces all face the proper direction
+ for(int j=0, len=faces.size(); j<len; j+=3){
+ vec3d vert[3];
+ for(int k=0; k<3; ++k){
+ int vert_index = faces[j+k];
+ vert[k] = triangle_points[vert_index].coord;
+ }
+ vec3d new_norm = normalize(cross(vert[1]-vert[0], vert[2]-vert[0]));
+ if(dot(new_norm, norm) < 0.0f){
+ std::swap(faces[j+1], faces[j+2]);
+ }
+ }
+ // Create unknown edge for each triangle side
+ for(int i=0, len=faces.size(); i<len; i+=3){
+ for(int j=0; j<3; ++j){
+ LabeledEdge labeled_edge;
+ labeled_edge.tri = i/3;
+ labeled_edge.points[0] = faces[i+j];
+ labeled_edge.points[1] = faces[i+(j+1)%3];
+ labeled_edge.points[2] = faces[i+(j+2)%3];
+ if(labeled_edge.points[0] > labeled_edge.points[1]){
+ std::swap(labeled_edge.points[0], labeled_edge.points[1]);
+ }
+ labeled_edge.id = labeled_edges.size();
+ labeled_edge.side = TrianglePoint::UNKNOWN;
+ labeled_edges.push_back(labeled_edge);
+ }
+ }
+ std::vector<TriNeighbor> tri_neighbors;
+ // Propagate edge info
+ {
+ std::vector<LabeledEdge> sorted = labeled_edges;
+ std::sort(sorted.begin(), sorted.end());
+ int old_index = 0;
+ for(int i=1, len=sorted.size(); i<=len; ++i){
+ if(i==len || sorted[i].points[0] != sorted[old_index].points[0] || sorted[i].points[1] != sorted[old_index].points[1]){
+ bool edge = false;
+ vec3d norm;
+ for(int j=old_index; j<i; ++j){
+ if(sorted[j].side == TrianglePoint::EDGE){
+ edge = true;
+ norm = sorted[j].norm;
+ }
+ }
+ if(edge){
+ for(int j=old_index; j<i; ++j){
+ if(sorted[j].side != TrianglePoint::EDGE && sorted[j].points[2] != -1){
+ int index = sorted[j].id;
+ labeled_edges[index].side = TrianglePoint::EDGE;
+ labeled_edges[index].norm = norm;
+ }
+ }
+ } else { // if edge is not an intersection edge, mark tri neighbors
+ for(int j=old_index; j<i; ++j){
+ if(sorted[j].points[2] == -1){
+ continue;
+ }
+ for(int k=old_index; k<i; ++k){
+ if(j == k || sorted[k].points[2] == -1){
+ continue;
+ }
+ tri_neighbors.push_back(TriNeighbor(sorted[j].tri, sorted[k].tri));
+ }
+ }
+ }
+ old_index = i;
+ }
+ }
+ }
+ std::vector<TrianglePoint::Side> triangle_side(faces.size()/3, TrianglePoint::UNKNOWN);
+ // Get outside/inside of edges neighboring edge
+ for(int i=0, len=labeled_edges.size(); i<len; ++i){
+ LabeledEdge &edge = labeled_edges[i];
+ if(edge.side == TrianglePoint::EDGE && edge.points[2] != -1){
+ vec3d opposite_vert = triangle_points[edge.points[2]].coord;
+ vec3d mid_point = (triangle_points[edge.points[0]].coord + triangle_points[edge.points[1]].coord)*0.5f;
+ vec3d dir = opposite_vert - mid_point;
+ if(dot(dir, edge.norm) > 0.0f){
+ triangle_side[edge.tri] = TrianglePoint::OUTSIDE;
+ } else {
+ triangle_side[edge.tri] = TrianglePoint::INSIDE;
+ }
+ continue;
+ }
+ }
+ // Propagate triangle side
+ std::sort(tri_neighbors.begin(), tri_neighbors.end());
+ std::vector<int> num_tri_neighbors(triangle_side.size(), 0);
+ std::vector<int> tri_neighbor_index(triangle_side.size(), -1);
+ for(int i=0, len=tri_neighbors.size(); i<len; ++i){
+ if(tri_neighbor_index[tri_neighbors[i].first] == -1){
+ tri_neighbor_index[tri_neighbors[i].first] = i;
+ }
+ ++num_tri_neighbors[tri_neighbors[i].first];
+ }
+ std::queue<int> tri_queue;
+ for(int i=0, len=triangle_side.size(); i<len; ++i){
+ if(triangle_side[i] != TrianglePoint::UNKNOWN){
+ tri_queue.push(i);
+ }
+ }
+ while(!tri_queue.empty()){
+ int tri = tri_queue.front();
+ for(int i=0; i<num_tri_neighbors[tri]; ++i){
+ int neighbor = tri_neighbors[tri_neighbor_index[tri]+i].second;
+ if(triangle_side[neighbor] == TrianglePoint::UNKNOWN){
+ triangle_side[neighbor] = triangle_side[tri];
+ tri_queue.push(neighbor);
+ }
+ }
+ tri_queue.pop();
+ }
+ for(int j=0, len=faces.size(); j<len; ++j){
+ output.new_faces->push_back(faces[j]+vert_start);
+ }
+ for(int j=0, len=triangle_side.size(); j<len; ++j){
+ output.new_triangle_sides->push_back(triangle_side[j]);
+ }
+ old_index = new_index;
+ }
+ }
+ }
+
+
+ struct MergeDist {
+ double dist;
+ int point[2];
+ };
+
+ bool operator<(const MergeDist &a, const MergeDist &b){
+ if(a.point[0] == b.point[0]){
+ return a.point[1] < b.point[1];
+ } else {
+ return a.point[0] < b.point[0];
+ }
+ }
+
+ void MergeTriIntersects(std::vector<TriIntersectInfo> &tri_intersect_info) {
+ std::vector<vec3d> test_points;
+ std::vector<MergeDist> test_point_distances;
+ for(int i=0, len=tri_intersect_info.size(); i<len; ++i){
+ TriIntersectInfo& tii = tri_intersect_info[i];
+ test_points.push_back(tii.tri_intersect.true_intersect[0]);
+ test_points.push_back(tii.tri_intersect.true_intersect[1]);
+ }
+ double merge_squared_threshold = 1.0e-10;
+ //double merge_threshold = 1.0e-5;
+ for(int i=0, len=test_points.size(); i<len; ++i){
+ for(int j=i+1, len=test_points.size(); j<len; ++j){
+ vec3d rel = test_points[i] - test_points[j];
+ MergeDist md;
+ md.dist = (dot(rel, rel));
+ md.point[0] = i;
+ md.point[1] = j;
+ if(md.point[0] > md.point[1]){
+ std::swap(md.point[0], md.point[1]);
+ }
+ if(md.dist < merge_squared_threshold){
+ test_point_distances.push_back(md);
+ }
+ }
+ }
+ std::sort(test_point_distances.begin(), test_point_distances.end());
+ for(int i=0, len=test_point_distances.size(); i<len; ++i){
+ MergeDist &tpd = test_point_distances[i];
+ test_points[tpd.point[1]] = test_points[tpd.point[0]];
+ }
+ for(int i=0, index=0, len=tri_intersect_info.size(); i<len; ++i, index+=2){
+ TriIntersectInfo& tii = tri_intersect_info[i];
+ tii.tri_intersect.true_intersect[0] = test_points[index+0];
+ tii.tri_intersect.true_intersect[1] = test_points[index+1];
+ }
+ }
+} // namespace ""
+
+bool CheckShapeValid(const std::vector<int> &faces, const std::vector<double> &vertices) {
+ std::vector<int> merged_faces = faces;
+ MergeDoubleVerts(vertices, merged_faces, true);
+ TriNeighborInfo tri_neighbor_info;
+ GetTriNeighbors(merged_faces, vertices, &tri_neighbor_info, true);
+ bool non_three_neighbor = false;
+ for(int i=0, len=tri_neighbor_info.num_tri_neighbors.size(); i<len; ++i){
+ if(tri_neighbor_info.num_tri_neighbors[i] != 3){
+ non_three_neighbor = true;
+ }
+ }
+ return !non_three_neighbor;
+}
+
+void AddCSGResult(const CSGResult &result, CSGModelInfo *csg_model, const Model& model, bool flip) {
+ csg_model->faces.reserve(csg_model->faces.size()+result.indices.size());
+ int old_faces = csg_model->verts.size()/3;
+ if(!flip){
+ for(int i=0, len=result.indices.size(); i<len; ++i){
+ csg_model->faces.push_back(result.indices[i] + old_faces);
+ }
+ } else {
+ for(int i=0, len=result.indices.size(); i<len; i+=3){
+ csg_model->faces.push_back(result.indices[i+0] + old_faces);
+ csg_model->faces.push_back(result.indices[i+2] + old_faces);
+ csg_model->faces.push_back(result.indices[i+1] + old_faces);
+ }
+ }
+ csg_model->verts.reserve(csg_model->verts.size()+result.verts.size());
+ csg_model->tex_coords.reserve(csg_model->verts.size()+result.verts.size()/3*2);
+ csg_model->tex_coords2.reserve(csg_model->verts.size()+result.verts.size()/3*2);
+ for(int i=0, len=result.bary.size(); i<len; i+=3){
+ const double *bary = &result.bary[i];
+ const double *result_vert = &result.verts[i];
+ const int *vert_id = &result.vert_id[i];
+ vec3d vert;
+ for(int j=0; j<3; ++j){
+ vert[j] = result_vert[j];
+ }
+ //MultiplyMat4Vec3D(eo->GetTransform(), &vert[0]);
+ csg_model->verts.push_back(vert[0]);
+ csg_model->verts.push_back(vert[1]);
+ csg_model->verts.push_back(vert[2]);
+
+ vec2 tex_coord;
+ for(int j=0; j<3; ++j){
+ vec2 tri_tex_coord;
+ int vert_index = vert_id[j]*2;
+ for(int k=0; k<2; ++k){
+ tri_tex_coord[k] = model.tex_coords[vert_index+k];
+ }
+ tex_coord += tri_tex_coord * (float)bary[j];
+ }
+ csg_model->tex_coords.push_back(tex_coord[0]);
+ csg_model->tex_coords.push_back(tex_coord[1]);
+
+ for(int j=0; j<3; ++j){
+ vec2 tri_tex_coord;
+ int vert_index = vert_id[j]*2;
+ for(int k=0; k<2; ++k){
+ tri_tex_coord[k] = model.tex_coords2[vert_index+k];
+ }
+ tex_coord += tri_tex_coord * (float)bary[j];
+ }
+ csg_model->tex_coords2.push_back(tex_coord[0]);
+ csg_model->tex_coords2.push_back(tex_coord[1]);
+ };
+}
+
+int ModelFromCSGModelInfo(const CSGModelInfo &model_info){
+ int model_id = Models::Instance()->AddModel();
+ Model *model = &Models::Instance()->GetModel(model_id);
+ model->vertices.resize(model_info.verts.size());
+ for(int i=0, len=model_info.verts.size(); i<len; ++i){
+ model->vertices[i] = (float)model_info.verts[i];
+ }
+ model->tex_coords.resize(model_info.verts.size()/3*2, 0.0f);
+ model->tex_coords2.resize(model_info.verts.size()/3*2, 0.0f);
+ for(int i=0, len=model_info.tex_coords.size(); i<len; ++i){
+ model->tex_coords[i] = model_info.tex_coords[i];
+ model->tex_coords2[i] = model_info.tex_coords2[i];
+ }
+ model->faces.resize(model_info.faces.size(), 0);
+ for(int i=0, len=model_info.faces.size(); i<len; ++i){
+ model->faces[i] = model_info.faces[i];
+ }
+ model->normals.resize(model_info.verts.size());
+ model->tangents.resize(model_info.verts.size());
+ model->bitangents.resize(model_info.verts.size());
+ model->face_normals.resize(model_info.faces.size()/3);
+ model->calcNormals();
+ model->calcTangents();
+ return model_id;
+}
+
+bool CollideObjects(BulletWorld& bw, const Model &model_a, const mat4 &transform_a, const Model &model_b, const mat4 &transform_b, CSGResultCombined *results) {
+ // Get transformed and merged meshes
+ TriIntersectionShape shape[2];
+ FillTriIntersectionShape(model_a, transform_a, &shape[0]);
+ FillTriIntersectionShape(model_b, transform_b, &shape[1]);
+ // Check triangle neighbors
+ if(!CheckShapeValid(shape[1].faces, shape[1].vertices)){
+ LOGE << "Model is invalid" << std::endl;
+ return false;
+ }
+
+ // Use Bullet GIMPACT collision to get candidate triangle pairs
+ MeshCollisionCallback cb;
+ bw.GetPairCollisions(shape[1].col_obj, shape[0].col_obj, cb);
+ // Process triangle intersections to eliminate false positives and get precise intersection segments
+ std::vector<TriIntersectInfo> tri_intersect_info;
+ for(MeshCollisionCallback::TriPairSet::iterator iter = cb.tri_pairs.begin(); iter != cb.tri_pairs.end(); ++iter){
+ // Get triangle vertices
+ vec3d tri[2][3];
+ const MeshCollisionCallback::TriPair &tri_pair = *iter;
+ int tri_index = tri_pair.first*3;
+ for(int j=0; j<3; ++j){
+ int vert_index = shape[1].faces[tri_index+j]*3;
+ for(int k=0; k<3; ++k){
+ tri[0][j][k] = shape[1].vertices[vert_index+k];
+ }
+ }
+ tri_index = tri_pair.second*3;
+ for(int j=0; j<3; ++j){
+ int vert_index = shape[0].faces[tri_index+j]*3;
+ for(int k=0; k<3; ++k){
+ tri[1][j][k] = shape[0].vertices[vert_index+k];
+ }
+ }
+ // Get intersection line
+ TriIntersect tri_intersect;
+ GetTriIntersection(tri, tri_intersect);
+ // Add to list
+ if(tri_intersect.valid){
+ TriIntersectInfo info;
+ info.tri_intersect = tri_intersect;
+ info.tris[0] = tri_pair.first;
+ info.tris[1] = tri_pair.second;
+ tri_intersect_info.push_back(info);
+ }
+ }
+ MergeTriIntersects(tri_intersect_info);
+ std::vector<int> new_faces[2];
+ std::vector<double> new_vertices[2];
+ std::vector<int> faces_to_remove[2];
+ std::vector<double> new_bary[2];
+ std::vector<int> new_vert_tri_points[2];
+ std::vector<TrianglePoint::Side> new_triangle_sides[2];
+ for(int i=0; i<2; ++i){
+ TriIntersectOutput output;
+ output.faces_to_remove = &faces_to_remove[i];
+ output.new_faces = &new_faces[i];
+ output.new_triangle_sides = &new_triangle_sides[i];
+ output.new_bary = &new_bary[i];
+ output.new_vert_tri_points = &new_vert_tri_points[i];
+ output.new_vertices = &new_vertices[i];
+ GetNewTris(i, tri_intersect_info, shape, output);
+ }
+ // Get old faces minus removed ones
+ std::vector<int> old_faces[2];
+ std::vector<double> old_vertices[2];
+ std::vector<double> old_bary[2];
+ std::vector<int> old_vert_id[2];
+ std::vector<TrianglePoint::Side> merge_triangle_sides[2];
+ for(int l=0; l<2; ++l){
+ old_faces[l].resize(shape[l].faces.size());
+ for(int i=0, len=shape[l].faces.size(); i<len; ++i){
+ old_faces[l][i] = shape[l].faces[i];
+ }
+ // Remove old intersecting faces
+ for(int i=faces_to_remove[1-l].size()-1; i>=0; --i){
+ int index = faces_to_remove[1-l][i]*3;
+ int last_index = old_faces[l].size()-3;
+ for(int j=0; j<3; ++j){
+ old_faces[l][index+j] = old_faces[l][last_index+j];
+ }
+ old_faces[l].resize(old_faces[l].size()-3);
+ }
+
+ old_vertices[l] = shape[l].vertices;
+ for(int i=0, len=old_vertices[l].size(); i<len; i+=3){
+ old_vert_id[l].push_back(i/3);
+ old_vert_id[l].push_back(i/3);
+ old_vert_id[l].push_back(i/3);
+ old_bary[l].push_back(1.0);
+ old_bary[l].push_back(0.0);
+ old_bary[l].push_back(0.0);
+ }
+
+ // Add new faces and vertices
+ merge_triangle_sides[l].resize(old_faces[l].size()/3, TrianglePoint::UNKNOWN);
+ for(int i=0, len=new_faces[1-l].size(); i<len; ++i){
+ old_faces[l].push_back(new_faces[1-l][i]+old_vertices[l].size()/3);
+ }
+ for(int i=0, len=new_triangle_sides[1-l].size(); i<len; ++i){
+ merge_triangle_sides[l].push_back(new_triangle_sides[1-l][i]);
+ }
+ old_vertices[l].insert(old_vertices[l].end(), new_vertices[1-l].begin(), new_vertices[1-l].end());
+ old_bary[l].insert(old_bary[l].end(), new_bary[1-l].begin(), new_bary[1-l].end());
+ old_vert_id[l].insert(old_vert_id[l].end(), new_vert_tri_points[1-l].begin(), new_vert_tri_points[1-l].end());
+
+ // Flood fill triangle sides
+ // -- merge overlapping verts
+ std::vector<int> merged_faces = old_faces[l];
+ MergeDoubleVerts(old_vertices[l], merged_faces, false);
+
+ TriNeighborInfo tri_neighbor_info;
+ GetTriNeighbors(merged_faces, shape[l].vertices, &tri_neighbor_info, false);
+
+ std::queue<int> tri_queue;
+ for(int i=0, len=merge_triangle_sides[l].size(); i<len; ++i){
+ if(merge_triangle_sides[l][i] != TrianglePoint::UNKNOWN){
+ tri_queue.push(i);
+ }
+ }
+ while(!tri_queue.empty()){
+ int tri = tri_queue.front();
+ for(int i=0; i<tri_neighbor_info.num_tri_neighbors[tri]; ++i){
+ int neighbor = tri_neighbor_info.tri_neighbors[tri_neighbor_info.tri_neighbor_index[tri]+i].second;
+ if(merge_triangle_sides[l][neighbor] == TrianglePoint::UNKNOWN){
+ merge_triangle_sides[l][neighbor] = merge_triangle_sides[l][tri];
+ tri_queue.push(neighbor);
+ }
+ }
+ tri_queue.pop();
+ }
+ }
+ if(!CheckShapeValid(shape[1].faces, shape[1].vertices)){
+ LOGE << "Model is invalid" << std::endl;
+ return false;
+ }
+ for(int which_model=0; which_model<2; ++which_model){
+ for(int which_side=0; which_side<2; ++which_side){
+ TrianglePoint::Side side;
+ switch(which_side){
+ case 0: side = TrianglePoint::INSIDE; break;
+ case 1: side = TrianglePoint::OUTSIDE; break;
+ default: __builtin_unreachable(); break;
+ }
+
+ const std::vector<TrianglePoint::Side> &sides = merge_triangle_sides[which_model];
+ std::vector<double> vertices;
+ std::vector<int> indices;
+ std::vector<double> bary;
+ std::vector<int> vert_id;
+ for(int i=0, side_index=0, len=old_faces[which_model].size(); i<len; i+=3, ++side_index){
+ if(sides[side_index] == side) {
+ for(int j=0; j<3; ++j){
+ int vert_index = old_faces[which_model][i+j]*3;
+ indices.push_back(vertices.size()/3);
+ for(int j=0; j<3; ++j){
+ vertices.push_back(old_vertices[which_model][vert_index+j]);
+ bary.push_back(old_bary[which_model][vert_index+j]);
+ vert_id.push_back(old_vert_id[which_model][vert_index+j]);
+ }
+ }
+ }
+ }
+ results->result[which_model][which_side].verts = vertices;
+ results->result[which_model][which_side].bary = bary;
+ results->result[which_model][which_side].vert_id = vert_id;
+ results->result[which_model][which_side].indices = indices;
+ }
+ }
+ return true;
+}
+
+void MultiplyMat4Vec3D(const mat4& mat, double vec[]) {
+ double new_vec[3];
+ const float *mat_e = mat.entries;
+ for(int i=0; i<3; ++i){
+ new_vec[i] = mat_e[0+i] * vec[0] + mat_e[4+i] * vec[1] + mat_e[8+i] * vec[2] + mat_e[12+i];
+ }
+ for(int i=0; i<3; ++i){
+ vec[i] = new_vec[i];
+ }
+}