💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

148 lines
5.2 KiB

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "triangle-mesh-operations.hpp"
float wrap(TriangleMesh::Triangle* triangle, Vector3 position, Vector3 direction, float length, std::vector<WrapOperationSegment>* 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;
}