💿🐜 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.
 

402 lines
10 KiB

/*
* Copyright (C) 2017 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 "mesh.hpp"
#include <algorithm>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <sstream>
#include <vector>
#include <limits>
bool loadHeightmap(const std::string& filename, glm::vec3 scale, WingedEdge* mesh)
{
int width;
int height;
int components;
// Load image data
unsigned char* pixels = stbi_load(filename.c_str(), &width, &height, &components, 1);
if (!pixels)
{
std::cerr << "Failed to load heightmap image \"" << filename << "\"\n";
return false;
}
std::size_t vertexCount = width * height;
std::size_t triangleCount = (width - 1) * (height - 1) * 2;
std::size_t indexCount = triangleCount * 3;
std::vector<glm::vec3> vertices(vertexCount);
std::vector<std::size_t> indices(indexCount);
// Adjust scale
scale.x *= 1.0f / ((float)width - 1);
scale.y *= 1.0f / 255.0f;
scale.z *= 1.0f / ((float)height - 1);
if (width > height) scale.z *= (float)height / (float) width;
else if (height > width) scale.x *= (float)width / (float)height;
// Calculate centered offset
glm::vec3 offset;
offset.x = (float)width * -0.5f * scale.x;
offset.y = 0.0f;
offset.z = (float)height * -0.5f * scale.z;
// Calculate vertex positions
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
std::size_t index = i * width + j;
glm::vec3* vertex = &vertices[index];
vertex->x = (float)j * scale.x + offset.x;
vertex->y = (float)pixels[index] * scale.y;
vertex->z = (float)i * scale.z + offset.z;
}
}
// Free loaded image
stbi_image_free(pixels);
// Generate indices
for (int i = 0; i < height - 1; ++i)
{
for (int j = 0; j < width - 1; ++j)
{
std::size_t a = i * width + j;
std::size_t b = (i + 1) * width + j;
std::size_t c = i * width + j + 1;
std::size_t d = (i + 1) * width + j + 1;
std::size_t index = (i * (width - 1) + j) * 2 * 3;
indices[index] = a;
indices[index + 1] = b;
indices[index + 2] = c;
indices[index + 3] = c;
indices[index + 4] = b;
indices[index + 5] = d;
}
}
return mesh->create(vertices, indices);
}
bool loadHeightmapBase(const std::string& filename, glm::vec3 scale, float floor, WingedEdge* mesh)
{
int width;
int height;
int components;
// Load image data
unsigned char* pixels = stbi_load(filename.c_str(), &width, &height, &components, 1);
if (!pixels)
{
std::cerr << "Failed to load heightmap image \"" << filename << "\"\n";
return false;
}
std::size_t vertexCount = width * 4 + height * 4;
std::size_t triangleCount = (width - 1) * 4 + (height - 1) * 4;
std::size_t indexCount = triangleCount * 3;
std::vector<glm::vec3> vertices(vertexCount);
std::vector<std::size_t> indices(indexCount);
// Adjust scale
scale.x *= 1.0f / ((float)width - 1);
scale.y *= 1.0f / 255.0f;
scale.z *= 1.0f / ((float)height - 1);
if (width > height) scale.z *= (float)height / (float) width;
else if (height > width) scale.x *= (float)width / (float)height;
// Calculate centered offset
glm::vec3 offset;
offset.x = (float)width * -0.5f * scale.x;
offset.y = 0.0f;
offset.z = (float)height * -0.5f * scale.z;
glm::vec3* vertex = &vertices[0];
// Top row
for (int j = 0; j < width; ++j)
{
int i = 0;
std::size_t index = i * width + j;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = (float)pixels[index] * scale.y;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = floor;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
}
// Bottom row
for (int j = 0; j < width; ++j)
{
int i = (height - 1);
std::size_t index = i * width + j;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = (float)pixels[index] * scale.y;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = floor;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
}
// Left column
for (int i = 0; i < height; ++i)
{
int j = 0;
std::size_t index = i * width + j;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = (float)pixels[index] * scale.y;
vertex->z = (float)i * scale.z + offset.z;
++vertex;;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = floor;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
}
// Right column
for (int i = 0; i < height; ++i)
{
int j = (width - 1);
std::size_t index = i * width + j;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = (float)pixels[index] * scale.y;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
vertex->x = (float)j * scale.x + offset.x;
vertex->y = floor;
vertex->z = (float)i * scale.z + offset.z;
++vertex;
}
// Free loaded image
stbi_image_free(pixels);
// Generate indices
std::size_t* index = &indices[0];
for (int i = 0; i < width - 1; ++i)
{
std::size_t a = i * 2;
std::size_t b = i * 2 + 1;
std::size_t c = (i + 1) * 2;
std::size_t d = (i + 1) * 2 + 1;
(*(index++)) = b;
(*(index++)) = a;
(*(index++)) = c;
(*(index++)) = b;
(*(index++)) = c;
(*(index++)) = d;
a += width * 2;
b += width * 2;
c += width * 2;
d += width * 2;
(*(index++)) = a;
(*(index++)) = b;
(*(index++)) = c;
(*(index++)) = c;
(*(index++)) = b;
(*(index++)) = d;
}
for (int i = 0; i < height - 1; ++i)
{
std::size_t a = width * 4 + i * 2;
std::size_t b = width * 4 + i * 2 + 1;
std::size_t c = width * 4 + (i + 1) * 2;
std::size_t d = width * 4 + (i + 1) * 2 + 1;
(*(index++)) = a;
(*(index++)) = b;
(*(index++)) = c;
(*(index++)) = c;
(*(index++)) = b;
(*(index++)) = d;
a += height * 2;
b += height * 2;
c += height * 2;
d += height * 2;
(*(index++)) = b;
(*(index++)) = a;
(*(index++)) = c;
(*(index++)) = b;
(*(index++)) = c;
(*(index++)) = d;
}
return mesh->create(vertices, indices);
}
void move(const WingedEdge* mesh, WingedEdge::Triangle* triangle, const glm::vec3& start, const glm::vec3& target, std::vector<WingedEdge::Triangle*>* visited, glm::vec3* end)
{
// Add triangle to visited list
visited->push_back(triangle);
// Grab triangle coordinates
const glm::vec3& a = triangle->edge->vertex->position;
const glm::vec3& b = triangle->edge->next->vertex->position;
const glm::vec3& c = triangle->edge->previous->vertex->position;
// Project target onto triangle
glm::vec3 closestPoint;
int edgeIndex = -1;
WingedEdge::Edge* closestEdge = nullptr;
project_on_triangle(target, a, b, c, &closestPoint, &edgeIndex);
*end = closestPoint;
// Determine if projected target is on an edge
switch (edgeIndex)
{
case -1:
// Projected target inside triangle
return;
case 0:
closestEdge = triangle->edge;
break;
case 1:
closestEdge = triangle->edge->next;
break;
case 2:
closestEdge = triangle->edge->previous;
break;
}
// If edge is not loose, repeat with connected triangle
if (closestEdge->symmetric != nullptr)
{
for (std::size_t i = 0; i < visited->size() - 1; ++i)
{
if ((*visited)[i] == closestEdge->symmetric->triangle)
return;
}
move(mesh, closestEdge->symmetric->triangle, closestPoint, target, visited, end);
}
}
std::tuple<bool, float, float, float> intersects(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& a, const glm::vec3& b, const glm::vec3& c)
{
// Find edges
glm::vec3 edge10 = b - a;
glm::vec3 edge20 = c - a;
// Calculate determinant
glm::vec3 pv = glm::cross(direction, edge20);
float det = glm::dot(edge10, pv);
if (!det)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
float inverseDet = 1.0f / det;
// Calculate u
glm::vec3 tv = origin - a;
float u = glm::dot(tv, pv) * inverseDet;
if (u < 0.0f || u > 1.0f)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
// Calculate v
glm::vec3 qv = glm::cross(tv, edge10);
float v = glm::dot(direction, qv) * inverseDet;
if (v < 0.0f || u + v > 1.0f)
{
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
// Calculate t
float t = glm::dot(edge20, qv) * inverseDet;
if (t > 0.0f)
{
return std::make_tuple(true, t, u, v);
}
return std::make_tuple(false, std::numeric_limits<float>::infinity(), 0.0f, 0.0f);
}
std::tuple<bool, float, float, std::size_t, std::size_t> intersects(const glm::vec3& origin, const glm::vec3& direction, const WingedEdge& mesh)
{
const std::vector<WingedEdge::Triangle*>* triangles = mesh.getTriangles();
bool intersection = false;
float t0 = std::numeric_limits<float>::infinity();
float t1 = -std::numeric_limits<float>::infinity();
std::size_t index0 = triangles->size();
std::size_t index1 = index0;
for (std::size_t i = 0; i < triangles->size(); ++i)
{
const WingedEdge::Triangle* triangle = (*triangles)[i];
const glm::vec3& a = triangle->edge->vertex->position;
const glm::vec3& b = triangle->edge->next->vertex->position;
const glm::vec3& c = triangle->edge->previous->vertex->position;
auto result = intersects(origin, direction, a, b, c);
if (std::get<0>(result))
{
intersection = true;
float t = std::get<1>(result);
float cosTheta = glm::dot(direction, triangle->normal);
if (cosTheta <= 0.0f)
{
// Front-facing
t0 = std::min<float>(t0, t);
index0 = i;
}
else
{
// Back-facing
t1 = std::max<float>(t1, t);
index1 = i;
}
}
}
return std::make_tuple(intersection, t0, t1, index0, index1);
}