/* * Copyright (C) 2017-2019 Christopher J. Howard * * This file is part of Antkeeper Source Code. * * Antkeeper Source Code 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 3 of the License, or * (at your option) any later version. * * Antkeeper Source Code 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 Antkeeper Source Code. If not, see . */ #include "triangle-mesh-operations.hpp" float wrap(TriangleMesh::Triangle* triangle, Vector3 position, Vector3 direction, float length, std::vector* segments) { // Get vertex positions and center of starting triangle const Vector3* a = &triangle->edge->vertex->position; const Vector3* b = &triangle->edge->next->vertex->position; const Vector3* c = &triangle->edge->previous->vertex->position; Vector3 center = ((*a) + (*b) + (*c)) * (1.0f / 3.0f); // Project start position and target position onto plane of the starting triangle Vector3 coplanarStart = projectOnPlane(position, center, triangle->normal); Vector3 coplanarTarget = projectOnPlane(position + direction * length, center, triangle->normal); // Constrain coplanar position and coplanar target to triangle bounds int edgeIndex = -1; int vertexIndex = -1; Vector3 barycentricStart = projectOnTriangle(coplanarStart, *a, *b, *c, &edgeIndex, &vertexIndex); Vector3 offset = cartesian(barycentricStart, *a, *b, *c) - coplanarStart; coplanarStart += offset; coplanarTarget += offset; // Calculate coplanar direction Vector3 coplanarDirection = glm::normalize(coplanarTarget - coplanarStart); // Form initial wrap operation segment WrapOperationSegment segment; segment.triangle = triangle; segment.startEdge = nullptr; segment.endEdge = nullptr; segment.startVertex = nullptr; segment.endVertex = nullptr; segment.startPosition = barycentricStart; segment.endPosition = barycentricStart; segment.length = 0.0f; // Determine starting edge if (edgeIndex == 0) segment.startEdge = triangle->edge; else if (edgeIndex == 1) segment.startEdge = triangle->edge->next; else if (edgeIndex == 2) segment.startEdge = triangle->edge->previous; // Determine starting vertex if (vertexIndex == 0) segment.startVertex = triangle->edge->vertex; else if (vertexIndex == 1) segment.startVertex = triangle->edge->next->vertex; else if (vertexIndex == 2) segment.startVertex = triangle->edge->previous->vertex; // Begin wrap operation float distance = 0.0f; while (1) { // Calculate coplanar Cartesian start and target positions coplanarStart = cartesian(segment.startPosition, *a, *b, *c); coplanarTarget = coplanarStart + coplanarDirection * (length - distance); // Calculate barycentric end position by projecting coplanar Cartesian target onto triangle segment.endPosition = projectOnTriangle(coplanarTarget, *a, *b, *c, &edgeIndex, &vertexIndex); // Determine ending edge segment.endEdge = nullptr; if (edgeIndex == 0) segment.endEdge = segment.triangle->edge; else if (edgeIndex == 1) segment.endEdge = segment.triangle->edge->next; else if (edgeIndex == 2) segment.endEdge = segment.triangle->edge->previous; // Determine ending vertex segment.endVertex = nullptr; if (vertexIndex == 0) segment.endVertex = segment.triangle->edge->vertex; else if (vertexIndex == 1) segment.endVertex = segment.triangle->edge->next->vertex; else if (vertexIndex == 2) segment.endVertex = segment.triangle->edge->previous->vertex; // Calculate coplanar Cartesian end position Vector3 coplanarEnd = cartesian(segment.endPosition, *a, *b, *c); // Determine distance traveled segment.length = glm::length(coplanarEnd - coplanarStart); distance += segment.length; // Add segment to wrapped segments segments->push_back(segment); // Check if wrap has completed if ((length - distance ) < 0.0001f || (!segment.endEdge && !segment.endVertex)) { distance = length; break; } // Check if a disconnectd edge was hit if (segment.endEdge && !segment.endEdge->symmetric) { break; } // Reorientate coplanar direction if (segment.triangle->normal != segment.endEdge->symmetric->triangle->normal) { coplanarDirection = glm::normalize(glm::rotation(segment.triangle->normal, segment.endEdge->symmetric->triangle->normal) * coplanarDirection); } // Move to the connected triangle segment.triangle = segment.endEdge->symmetric->triangle; segment.startEdge = segment.endEdge->symmetric; segment.endEdge = nullptr; segment.startVertex = nullptr; segment.endVertex = nullptr; // Get vertex positions of triangle a = &segment.triangle->edge->vertex->position; b = &segment.triangle->edge->next->vertex->position; c = &segment.triangle->edge->previous->vertex->position; // Calculate barycentric starting position of the next segment segment.startPosition = normalize_barycentric(barycentric(coplanarEnd, *a, *b, *c)); } return distance; }