/* * 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 . */ #include "mesh.hpp" #include #include #include #include #include #include #include #include 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 vertices(vertexCount); std::vector 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 vertices(vertexCount); std::vector 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* 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 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::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::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::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::infinity(), 0.0f, 0.0f); } std::tuple intersects(const glm::vec3& origin, const glm::vec3& direction, const WingedEdge& mesh) { const std::vector* triangles = mesh.getTriangles(); bool intersection = false; float t0 = std::numeric_limits::infinity(); float t1 = -std::numeric_limits::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(t0, t); index0 = i; } else { // Back-facing t1 = std::max(t1, t); index1 = i; } } } return std::make_tuple(intersection, t0, t1, index0, index1); }