|
|
- /*
- * 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);
- }
|