From 2c786e0718c55d81e58c917b42f35c1c98629dd3 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 16 Jan 2024 12:47:24 +0100 Subject: [PATCH] Remove original lightmapper implementation --- src/lightmap/collision.cpp | 973 ---------------- src/lightmap/collision.h | 207 ---- src/lightmap/delauneytriangulator.cpp | 250 ---- src/lightmap/delauneytriangulator.h | 45 - src/lightmap/glsl_frag.h | 566 --------- src/lightmap/glsl_frag_resolve.h | 49 - src/lightmap/glsl_vert.h | 26 - src/lightmap/gpuraytracer.cpp | 1117 ------------------ src/lightmap/gpuraytracer.h | 228 ---- src/lightmap/levelmesh.cpp | 1546 ------------------------- src/lightmap/levelmesh.h | 195 ---- src/lightmap/lightmaptexture.cpp | 13 - src/lightmap/lightmaptexture.h | 20 - src/lightmap/pngwriter.cpp | 218 ---- src/lightmap/pngwriter.h | 78 -- src/lightmap/portal.h | 73 -- src/lightmap/renderdoc_app.h | 692 ----------- src/lightmap/stacktrace.cpp | 198 ---- src/lightmap/stacktrace.h | 6 - src/lightmap/surfaceclip.cpp | 108 -- src/lightmap/surfaceclip.h | 28 - 21 files changed, 6636 deletions(-) delete mode 100644 src/lightmap/collision.cpp delete mode 100644 src/lightmap/collision.h delete mode 100644 src/lightmap/delauneytriangulator.cpp delete mode 100644 src/lightmap/delauneytriangulator.h delete mode 100644 src/lightmap/glsl_frag.h delete mode 100644 src/lightmap/glsl_frag_resolve.h delete mode 100644 src/lightmap/glsl_vert.h delete mode 100644 src/lightmap/gpuraytracer.cpp delete mode 100644 src/lightmap/gpuraytracer.h delete mode 100644 src/lightmap/levelmesh.cpp delete mode 100644 src/lightmap/levelmesh.h delete mode 100644 src/lightmap/lightmaptexture.cpp delete mode 100644 src/lightmap/lightmaptexture.h delete mode 100644 src/lightmap/pngwriter.cpp delete mode 100644 src/lightmap/pngwriter.h delete mode 100644 src/lightmap/portal.h delete mode 100644 src/lightmap/renderdoc_app.h delete mode 100644 src/lightmap/stacktrace.cpp delete mode 100644 src/lightmap/stacktrace.h delete mode 100644 src/lightmap/surfaceclip.cpp delete mode 100644 src/lightmap/surfaceclip.h diff --git a/src/lightmap/collision.cpp b/src/lightmap/collision.cpp deleted file mode 100644 index fd87e59..0000000 --- a/src/lightmap/collision.cpp +++ /dev/null @@ -1,973 +0,0 @@ -/* -** ZDRay collision -** Copyright (c) 2018 Magnus Norddahl -** -** This software is provided 'as-is', without any express or implied -** warranty. In no event will the authors be held liable for any damages -** arising from the use of this software. -** -** Permission is granted to anyone to use this software for any purpose, -** including commercial applications, and to alter it and redistribute it -** freely, subject to the following restrictions: -** -** 1. The origin of this software must not be misrepresented; you must not -** claim that you wrote the original software. If you use this software -** in a product, an acknowledgment in the product documentation would be -** appreciated but is not required. -** 2. Altered source versions must be plainly marked as such, and must not be -** misrepresented as being the original software. -** 3. This notice may not be removed or altered from any source distribution. -** -*/ - -#include "collision.h" -#include -#include -#include -#ifndef NO_SSE -#include -#endif - -TriangleMeshShape::TriangleMeshShape(const vec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements) - : vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements) -{ - int num_triangles = num_elements / 3; - if (num_triangles <= 0) - return; - - std::vector triangles; - std::vector centroids; - triangles.reserve(num_triangles); - centroids.reserve(num_triangles); - for (int i = 0; i < num_triangles; i++) - { - triangles.push_back(i); - - int element_index = i * 3; - vec3 centroid = (vertices[elements[element_index + 0]] + vertices[elements[element_index + 1]] + vertices[elements[element_index + 2]]) * (1.0f / 3.0f); - centroids.push_back(centroid); - } - - std::vector work_buffer(num_triangles * 2); - - root = subdivide(&triangles[0], (int)triangles.size(), ¢roids[0], &work_buffer[0]); -} - -float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, const vec3 &target) -{ - return sweep(shape1, shape2, shape1->root, target); -} - -bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2) -{ - return find_any_hit(shape1, shape2, shape1->root, shape2->root); -} - -bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2) -{ - return find_any_hit(shape1, shape2, shape1->root); -} - -std::vector TriangleMeshShape::find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2) -{ - std::vector hits; - find_all_hits(shape1, shape2, shape1->root, hits); - return hits; -} - -bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end) -{ - return find_any_hit(shape, RayBBox(ray_start, ray_end), shape->root); -} - -TraceHit TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end) -{ - TraceHit hit; - - // Perform segmented tracing to keep the ray AABB box smaller - - vec3 ray_dir = ray_end - ray_start; - float tracedist = length(ray_dir); - float segmentlen = std::max(100.0f, tracedist / 20.0f); - for (float t = 0.0f; t < tracedist; t += segmentlen) - { - float segstart = t / tracedist; - float segend = std::min(t + segmentlen, tracedist) / tracedist; - - find_first_hit(shape, RayBBox(ray_start + ray_dir * segstart, ray_start + ray_dir * segend), shape->root, &hit); - if (hit.fraction < 1.0f) - { - hit.fraction = segstart * (1.0f - hit.fraction) + segend * hit.fraction; - break; - } - } - - return hit; -} - -float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target) -{ - if (sweep_overlap_bv_sphere(shape1, shape2, a, target)) - { - if (shape1->is_leaf(a)) - { - return sweep_intersect_triangle_sphere(shape1, shape2, a, target); - } - else - { - return std::min(sweep(shape1, shape2, shape1->nodes[a].left, target), sweep(shape1, shape2, shape1->nodes[a].right, target)); - } - } - return 1.0f; -} - -bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2, int a) -{ - if (overlap_bv_sphere(shape1, shape2, a)) - { - if (shape1->is_leaf(a)) - { - return overlap_triangle_sphere(shape1, shape2, a); - } - else - { - if (find_any_hit(shape1, shape2, shape1->nodes[a].left)) - return true; - else - return find_any_hit(shape1, shape2, shape1->nodes[a].right); - } - } - return false; -} - -void TriangleMeshShape::find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2, int a, std::vector& hits) -{ - if (overlap_bv_sphere(shape1, shape2, a)) - { - if (shape1->is_leaf(a)) - { - if (overlap_triangle_sphere(shape1, shape2, a)) - { - hits.push_back(shape1->nodes[a].element_index / 3); - } - } - else - { - find_all_hits(shape1, shape2, shape1->nodes[a].left, hits); - find_all_hits(shape1, shape2, shape1->nodes[a].right, hits); - } - } -} - -bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b) -{ - bool leaf_a = shape1->is_leaf(a); - bool leaf_b = shape2->is_leaf(b); - if (leaf_a && leaf_b) - { - return overlap_triangle_triangle(shape1, shape2, a, b); - } - else if (!leaf_a && !leaf_b) - { - if (overlap_bv(shape1, shape2, a, b)) - { - if (shape1->volume(a) > shape2->volume(b)) - { - if (find_any_hit(shape1, shape2, shape1->nodes[a].left, b)) - return true; - else - return find_any_hit(shape1, shape2, shape1->nodes[a].right, b); - } - else - { - if (find_any_hit(shape1, shape2, a, shape2->nodes[b].left)) - return true; - else - return find_any_hit(shape1, shape2, a, shape2->nodes[b].right); - } - } - return false; - } - else if (leaf_a) - { - if (overlap_bv_triangle(shape2, shape1, b, a)) - { - if (find_any_hit(shape1, shape2, a, shape2->nodes[b].left)) - return true; - else - return find_any_hit(shape1, shape2, a, shape2->nodes[b].right); - } - return false; - } - else - { - if (overlap_bv_triangle(shape1, shape2, a, b)) - { - if (find_any_hit(shape1, shape2, shape1->nodes[a].left, b)) - return true; - else - return find_any_hit(shape1, shape2, shape1->nodes[a].right, b); - } - return false; - } -} - -bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const RayBBox &ray, int a) -{ - if (overlap_bv_ray(shape, ray, a)) - { - if (shape->is_leaf(a)) - { - float baryB, baryC; - return intersect_triangle_ray(shape, ray, a, baryB, baryC) < 1.0f; - } - else - { - if (find_any_hit(shape, ray, shape->nodes[a].left)) - return true; - else - return find_any_hit(shape, ray, shape->nodes[a].right); - } - } - return false; -} - -void TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const RayBBox &ray, int a, TraceHit *hit) -{ - if (overlap_bv_ray(shape, ray, a)) - { - if (shape->is_leaf(a)) - { - float baryB, baryC; - float t = intersect_triangle_ray(shape, ray, a, baryB, baryC); - if (t < hit->fraction) - { - hit->fraction = t; - hit->triangle = shape->nodes[a].element_index / 3; - hit->b = baryB; - hit->c = baryC; - } - } - else - { - find_first_hit(shape, ray, shape->nodes[a].left, hit); - find_first_hit(shape, ray, shape->nodes[a].right, hit); - } - } -} - -bool TriangleMeshShape::overlap_bv_ray(TriangleMeshShape *shape, const RayBBox &ray, int a) -{ - return IntersectionTest::ray_aabb(ray, shape->nodes[a].aabb) == IntersectionTest::overlap; -} - -float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a, float &barycentricB, float &barycentricC) -{ - const int start_element = shape->nodes[a].element_index; - - vec3 p[3] = - { - shape->vertices[shape->elements[start_element]], - shape->vertices[shape->elements[start_element + 1]], - shape->vertices[shape->elements[start_element + 2]] - }; - - // Moeller–Trumbore ray-triangle intersection algorithm: - - vec3 D = ray.end - ray.start; - - // Find vectors for two edges sharing p[0] - vec3 e1 = p[1] - p[0]; - vec3 e2 = p[2] - p[0]; - - // Begin calculating determinant - also used to calculate u parameter - vec3 P = cross(D, e2); - float det = dot(e1, P); - - // Backface check - //if (det < 0.0f) - // return 1.0f; - - // If determinant is near zero, ray lies in plane of triangle - if (det > -FLT_EPSILON && det < FLT_EPSILON) - return 1.0f; - - float inv_det = 1.0f / det; - - // Calculate distance from p[0] to ray origin - vec3 T = ray.start - p[0]; - - // Calculate u parameter and test bound - float u = dot(T, P) * inv_det; - - // Check if the intersection lies outside of the triangle - if (u < 0.f || u > 1.f) - return 1.0f; - - // Prepare to test v parameter - vec3 Q = cross(T, e1); - - // Calculate V parameter and test bound - float v = dot(D, Q) * inv_det; - - // The intersection lies outside of the triangle - if (v < 0.f || u + v > 1.f) - return 1.0f; - - float t = dot(e2, Q) * inv_det; - if (t <= FLT_EPSILON) - return 1.0f; - - // Return hit location on triangle in barycentric coordinates - barycentricB = u; - barycentricC = v; - - return t; -} - -bool TriangleMeshShape::sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target) -{ - // Convert to ray test by expanding the AABB: - - CollisionBBox aabb = shape1->nodes[a].aabb; - aabb.Extents += shape2->radius; - - return IntersectionTest::ray_aabb(RayBBox(shape2->center, target), aabb) == IntersectionTest::overlap; -} - -float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target) -{ - const int start_element = shape1->nodes[a].element_index; - - vec3 p[3] = - { - shape1->vertices[shape1->elements[start_element]], - shape1->vertices[shape1->elements[start_element + 1]], - shape1->vertices[shape1->elements[start_element + 2]] - }; - - vec3 c = shape2->center; - vec3 e = target; - float r = shape2->radius; - - // Dynamic intersection test between a ray and the minkowski sum of the sphere and polygon: - - vec3 n = normalize(cross(p[1] - p[0], p[2] - p[0])); - vec4 plane(n, -dot(n, p[0])); - - // Step 1: Plane intersect test - - float sc = dot(plane, vec4(c, 1.0f)); - float se = dot(plane, vec4(e, 1.0f)); - bool same_side = sc * se > 0.0f; - - if (same_side && std::abs(sc) > r && std::abs(se) > r) - return 1.0f; - - // Step 1a: Check if point is in polygon (using crossing ray test in 2d) - { - float t = (sc - r) / (sc - se); - - vec3 vt = c + (e - c) * t; - - vec3 u0 = p[1] - p[0]; - vec3 u1 = p[2] - p[0]; - - vec2 v_2d[3] = - { - vec2(0.0f, 0.0f), - vec2(dot(u0, u0), 0.0f), - vec2(0.0f, dot(u1, u1)) - }; - - vec2 point(dot(u0, vt), dot(u1, vt)); - - bool inside = false; - vec2 e0 = v_2d[2]; - bool y0 = e0.y >= point.y; - for (int i = 0; i < 3; i++) - { - vec2 e1 = v_2d[i]; - bool y1 = e1.y >= point.y; - - if (y0 != y1 && ((e1.y - point.y) * (e0.x - e1.x) >= (e1.x - point.x) * (e0.y - e1.y)) == y1) - inside = !inside; - - y0 = y1; - e0 = e1; - } - - if (inside) - return t; - } - - // Step 2: Edge intersect test - - vec3 ke[3] = - { - p[1] - p[0], - p[2] - p[1], - p[0] - p[2], - }; - - vec3 kg[3] = - { - p[0] - c, - p[1] - c, - p[2] - c, - }; - - vec3 ks = e - c; - - float kgg[3]; - float kgs[3]; - float kss[3]; - - for (int i = 0; i < 3; i++) - { - float kee = dot(ke[i], ke[i]); - float keg = dot(ke[i], kg[i]); - float kes = dot(ke[i], ks); - kgg[i] = dot(kg[i], kg[i]); - kgs[i] = dot(kg[i], ks); - kss[i] = dot(ks, ks); - - float aa = kee * kss[i] - kes * kes; - float bb = 2 * (keg * kes - kee * kgs[i]); - float cc = kee * (kgg[i] - r * r) - keg * keg; - - float sign = (bb >= 0.0f) ? 1.0f : -1.0f; - float q = -0.5f * (bb + sign * std::sqrt(bb * bb - 4 * aa * cc)); - float t0 = q / aa; - float t1 = cc / q; - - float t; - if (t0 < 0.0f || t0 > 1.0f) - t = t1; - else if (t1 < 0.0f || t1 > 1.0f) - t = t0; - else - t = std::min(t0, t1); - - if (t >= 0.0f && t <= 1.0f) - { - vec3 ct = c + ks * t; - float d = dot(ct - p[i], ke[i]); - if (d >= 0.0f && d <= kee) - return t; - } - } - - // Step 3: Point intersect test - - for (int i = 0; i < 3; i++) - { - float aa = kss[i]; - float bb = -2.0f * kgs[i]; - float cc = kgg[i] - r * r; - - float sign = (bb >= 0.0f) ? 1.0f : -1.0f; - float q = -0.5f * (bb + sign * std::sqrt(bb * bb - 4 * aa * cc)); - float t0 = q / aa; - float t1 = cc / q; - - float t; - if (t0 < 0.0f || t0 > 1.0f) - t = t1; - else if (t1 < 0.0f || t1 > 1.0f) - t = t0; - else - t = std::min(t0, t1); - - if (t >= 0.0f && t <= 1.0f) - return t; - } - - return 1.0f; -} - -bool TriangleMeshShape::overlap_bv(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b) -{ - return IntersectionTest::aabb(shape1->nodes[a].aabb, shape2->nodes[b].aabb) == IntersectionTest::overlap; -} - -bool TriangleMeshShape::overlap_bv_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b) -{ - return false; -} - -bool TriangleMeshShape::overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a) -{ - return IntersectionTest::sphere_aabb(shape2->center, shape2->radius, shape1->nodes[a].aabb) == IntersectionTest::overlap; -} - -bool TriangleMeshShape::overlap_triangle_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b) -{ - return false; -} - -bool TriangleMeshShape::overlap_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int shape1_node_index) -{ - // http://realtimecollisiondetection.net/blog/?p=103 - - int element_index = shape1->nodes[shape1_node_index].element_index; - - vec3 P = shape2->center; - vec3 A = shape1->vertices[shape1->elements[element_index]] - P; - vec3 B = shape1->vertices[shape1->elements[element_index + 1]] - P; - vec3 C = shape1->vertices[shape1->elements[element_index + 2]] - P; - float r = shape2->radius; - float rr = r * r; - - // Testing if sphere lies outside the triangle plane - vec3 V = cross(B - A, C - A); - float d = dot(A, V); - float e = dot(V, V); - bool sep1 = d * d > rr * e; - - // Testing if sphere lies outside a triangle vertex - float aa = dot(A, A); - float ab = dot(A, B); - float ac = dot(A, C); - float bb = dot(B, B); - float bc = dot(B, C); - float cc = dot(C, C); - bool sep2 = (aa > rr) && (ab > aa) && (ac > aa); - bool sep3 = (bb > rr) && (ab > bb) && (bc > bb); - bool sep4 = (cc > rr) && (ac > cc) && (bc > cc); - - // Testing if sphere lies outside a triangle edge - vec3 AB = B - A; - vec3 BC = C - B; - vec3 CA = A - C; - float d1 = ab - aa; - float d2 = bc - bb; - float d3 = ac - cc; - float e1 = dot(AB, AB); - float e2 = dot(BC, BC); - float e3 = dot(CA, CA); - vec3 Q1 = A * e1 - AB * d1; - vec3 Q2 = B * e2 - BC * d2; - vec3 Q3 = C * e3 - CA * d3; - vec3 QC = C * e1 - Q1; - vec3 QA = A * e2 - Q2; - vec3 QB = B * e3 - Q3; - bool sep5 = (dot(Q1, Q1) > rr * e1 * e1) && (dot(Q1, QC) > 0.0f); - bool sep6 = (dot(Q2, Q2) > rr * e2 * e2) && (dot(Q2, QA) > 0.0f); - bool sep7 = (dot(Q3, Q3) > rr * e3 * e3) && (dot(Q3, QB) > 0.0f); - - bool separated = sep1 || sep2 || sep3 || sep4 || sep5 || sep6 || sep7; - return (!separated); -} - -bool TriangleMeshShape::is_leaf(int node_index) -{ - return nodes[node_index].element_index != -1; -} - -float TriangleMeshShape::volume(int node_index) -{ - const vec3 &extents = nodes[node_index].aabb.Extents; - return extents.x * extents.y * extents.z; -} - -int TriangleMeshShape::get_min_depth() const -{ - std::function visit; - visit = [&](int level, int node_index) -> int { - const Node &node = nodes[node_index]; - if (node.element_index == -1) - return std::min(visit(level + 1, node.left), visit(level + 1, node.right)); - else - return level; - }; - return visit(1, root); -} - -int TriangleMeshShape::get_max_depth() const -{ - std::function visit; - visit = [&](int level, int node_index) -> int { - const Node &node = nodes[node_index]; - if (node.element_index == -1) - return std::max(visit(level + 1, node.left), visit(level + 1, node.right)); - else - return level; - }; - return visit(1, root); -} - -float TriangleMeshShape::get_average_depth() const -{ - std::function visit; - visit = [&](int level, int node_index) -> float { - const Node &node = nodes[node_index]; - if (node.element_index == -1) - return visit(level + 1, node.left) + visit(level + 1, node.right); - else - return (float)level; - }; - float depth_sum = visit(1, root); - int leaf_count = (num_elements / 3); - return depth_sum / leaf_count; -} - -float TriangleMeshShape::get_balanced_depth() const -{ - return std::log2((float)(num_elements / 3)); -} - -int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const vec3 *centroids, int *work_buffer) -{ - if (num_triangles == 0) - return -1; - - // Find bounding box and median of the triangle centroids - vec3 median; - vec3 min, max; - min = vertices[elements[triangles[0] * 3]]; - max = min; - for (int i = 0; i < num_triangles; i++) - { - int element_index = triangles[i] * 3; - for (int j = 0; j < 3; j++) - { - const vec3 &vertex = vertices[elements[element_index + j]]; - - min.x = std::min(min.x, vertex.x); - min.y = std::min(min.y, vertex.y); - min.z = std::min(min.z, vertex.z); - - max.x = std::max(max.x, vertex.x); - max.y = std::max(max.y, vertex.y); - max.z = std::max(max.z, vertex.z); - } - - median += centroids[triangles[i]]; - } - median /= (float)num_triangles; - - if (num_triangles == 1) // Leaf node - { - nodes.push_back(Node(min, max, triangles[0] * 3)); - return (int)nodes.size() - 1; - } - - // Find the longest axis - float axis_lengths[3] = - { - max.x - min.x, - max.y - min.y, - max.z - min.z - }; - - int axis_order[3] = { 0, 1, 2 }; - std::sort(axis_order, axis_order + 3, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; }); - - // Try split at longest axis, then if that fails the next longest, and then the remaining one - int left_count, right_count; - vec3 axis; - for (int attempt = 0; attempt < 3; attempt++) - { - // Find the split plane for axis - switch (axis_order[attempt]) - { - default: - case 0: axis = vec3(1.0f, 0.0f, 0.0f); break; - case 1: axis = vec3(0.0f, 1.0f, 0.0f); break; - case 2: axis = vec3(0.0f, 0.0f, 1.0f); break; - } - vec4 plane(axis, -dot(median, axis)); - - // Split triangles into two - left_count = 0; - right_count = 0; - for (int i = 0; i < num_triangles; i++) - { - int triangle = triangles[i]; - int element_index = triangle * 3; - - float side = dot(vec4(centroids[triangles[i]], 1.0f), plane); - if (side >= 0.0f) - { - work_buffer[left_count] = triangle; - left_count++; - } - else - { - work_buffer[num_triangles + right_count] = triangle; - right_count++; - } - } - - if (left_count != 0 && right_count != 0) - break; - } - - // Check if something went wrong when splitting and do a random split instead - if (left_count == 0 || right_count == 0) - { - left_count = num_triangles / 2; - right_count = num_triangles - left_count; - } - else - { - // Move result back into triangles list: - for (int i = 0; i < left_count; i++) - triangles[i] = work_buffer[i]; - for (int i = 0; i < right_count; i++) - triangles[i + left_count] = work_buffer[num_triangles + i]; - } - - // Create child nodes: - int left_index = -1; - int right_index = -1; - if (left_count > 0) - left_index = subdivide(triangles, left_count, centroids, work_buffer); - if (right_count > 0) - right_index = subdivide(triangles + left_count, right_count, centroids, work_buffer); - - nodes.push_back(Node(min, max, left_index, right_index)); - return (int)nodes.size() - 1; -} - -///////////////////////////////////////////////////////////////////////////// - -IntersectionTest::Result IntersectionTest::plane_aabb(const vec4 &plane, const BBox &aabb) -{ - vec3 center = aabb.Center(); - vec3 extents = aabb.Extents(); - float e = extents.x * std::abs(plane.x) + extents.y * std::abs(plane.y) + extents.z * std::abs(plane.z); - float s = center.x * plane.x + center.y * plane.y + center.z * plane.z + plane.w; - if (s - e > 0) - return inside; - else if (s + e < 0) - return outside; - else - return intersecting; -} - -IntersectionTest::Result IntersectionTest::plane_obb(const vec4 &plane, const OrientedBBox &obb) -{ - vec3 n = plane.xyz(); - float d = plane.w; - float e = obb.Extents.x * std::abs(dot(obb.axis_x, n)) + obb.Extents.y * std::abs(dot(obb.axis_y, n)) + obb.Extents.z * std::abs(dot(obb.axis_z, n)); - float s = dot(obb.Center, n) + d; - if (s - e > 0) - return inside; - else if (s + e < 0) - return outside; - else - return intersecting; -} - -IntersectionTest::OverlapResult IntersectionTest::sphere(const vec3 ¢er1, float radius1, const vec3 ¢er2, float radius2) -{ - vec3 h = center1 - center2; - float square_distance = dot(h, h); - float radius_sum = radius1 + radius2; - if (square_distance > radius_sum * radius_sum) - return disjoint; - else - return overlap; -} - -IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const vec3 ¢er, float radius, const BBox &aabb) -{ - vec3 a = aabb.min - center; - vec3 b = center - aabb.max; - a.x = std::max(a.x, 0.0f); - a.y = std::max(a.y, 0.0f); - a.z = std::max(a.z, 0.0f); - b.x = std::max(b.x, 0.0f); - b.y = std::max(b.y, 0.0f); - b.z = std::max(b.z, 0.0f); - vec3 e = a + b; - float d = dot(e, e); - if (d > radius * radius) - return disjoint; - else - return overlap; -} - -IntersectionTest::OverlapResult IntersectionTest::aabb(const BBox &a, const BBox &b) -{ - if (a.min.x > b.max.x || b.min.x > a.max.x || - a.min.y > b.max.y || b.min.y > a.max.y || - a.min.z > b.max.z || b.min.z > a.max.z) - { - return disjoint; - } - else - { - return overlap; - } -} - -IntersectionTest::Result IntersectionTest::frustum_aabb(const FrustumPlanes &frustum, const BBox &box) -{ - bool is_intersecting = false; - for (int i = 0; i < 6; i++) - { - Result result = plane_aabb(frustum.planes[i], box); - if (result == outside) - return outside; - else if (result == intersecting) - is_intersecting = true; - break; - } - if (is_intersecting) - return intersecting; - else - return inside; -} - -IntersectionTest::Result IntersectionTest::frustum_obb(const FrustumPlanes &frustum, const OrientedBBox &box) -{ - bool is_intersecting = false; - for (int i = 0; i < 6; i++) - { - Result result = plane_obb(frustum.planes[i], box); - if (result == outside) - return outside; - else if (result == intersecting) - is_intersecting = true; - } - if (is_intersecting) - return intersecting; - else - return inside; -} - -static const uint32_t clearsignbitmask[] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }; - -IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const RayBBox &ray, const CollisionBBox &aabb) -{ -#ifndef NO_SSE - - __m128 v = _mm_loadu_ps(&ray.v.x); - __m128 w = _mm_loadu_ps(&ray.w.x); - __m128 h = _mm_loadu_ps(&aabb.Extents.x); - __m128 c = _mm_sub_ps(_mm_loadu_ps(&ray.c.x), _mm_loadu_ps(&aabb.Center.x)); - - __m128 clearsignbit = _mm_loadu_ps(reinterpret_cast(clearsignbitmask)); - - __m128 abs_c = _mm_and_ps(c, clearsignbit); - int mask = _mm_movemask_ps(_mm_cmpgt_ps(abs_c, _mm_add_ps(v, h))); - if (mask & 7) - return disjoint; - - __m128 c1 = _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 0, 0, 1)); // c.y, c.x, c.x - __m128 c2 = _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 1, 2, 2)); // c.z, c.z, c.y - __m128 w1 = _mm_shuffle_ps(w, w, _MM_SHUFFLE(3, 1, 2, 2)); // w.z, w.z, w.y - __m128 w2 = _mm_shuffle_ps(w, w, _MM_SHUFFLE(3, 0, 0, 1)); // w.y, w.x, w.x - __m128 lhs = _mm_and_ps(_mm_sub_ps(_mm_mul_ps(c1, w1), _mm_mul_ps(c2, w2)), clearsignbit); - - __m128 h1 = _mm_shuffle_ps(h, h, _MM_SHUFFLE(3, 0, 0, 1)); // h.y, h.x, h.x - __m128 h2 = _mm_shuffle_ps(h, h, _MM_SHUFFLE(3, 1, 2, 2)); // h.z, h.z, h.y - __m128 v1 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 1, 2, 2)); // v.z, v.z, v.y - __m128 v2 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 0, 0, 1)); // v.y, v.x, v.x - __m128 rhs = _mm_add_ps(_mm_mul_ps(h1, v1), _mm_mul_ps(h2, v2)); - - mask = _mm_movemask_ps(_mm_cmpgt_ps(lhs, rhs)); - return (mask & 7) ? disjoint : overlap; - -#else - const vec3 &v = ray.v; - const vec3 &w = ray.w; - const vec3 &h = aabb.Extents; - auto c = ray.c - aabb.Center; - - if (std::abs(c.x) > v.x + h.x || std::abs(c.y) > v.y + h.y || std::abs(c.z) > v.z + h.z) - return disjoint; - - if (std::abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || - std::abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || - std::abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) - return disjoint; - - return overlap; -#endif -} - -///////////////////////////////////////////////////////////////////////////// - -FrustumPlanes::FrustumPlanes() -{ -} - -FrustumPlanes::FrustumPlanes(const mat4 &world_to_projection) -{ - planes[0] = near_frustum_plane(world_to_projection); - planes[1] = far_frustum_plane(world_to_projection); - planes[2] = left_frustum_plane(world_to_projection); - planes[3] = right_frustum_plane(world_to_projection); - planes[4] = top_frustum_plane(world_to_projection); - planes[5] = bottom_frustum_plane(world_to_projection); -} - -vec4 FrustumPlanes::left_frustum_plane(const mat4 &matrix) -{ - vec4 plane( - matrix[3 + 0 * 4] + matrix[0 + 0 * 4], - matrix[3 + 1 * 4] + matrix[0 + 1 * 4], - matrix[3 + 2 * 4] + matrix[0 + 2 * 4], - matrix[3 + 3 * 4] + matrix[0 + 3 * 4]); - plane /= length(plane.xyz()); - return plane; -} - -vec4 FrustumPlanes::right_frustum_plane(const mat4 &matrix) -{ - vec4 plane( - matrix[3 + 0 * 4] - matrix[0 + 0 * 4], - matrix[3 + 1 * 4] - matrix[0 + 1 * 4], - matrix[3 + 2 * 4] - matrix[0 + 2 * 4], - matrix[3 + 3 * 4] - matrix[0 + 3 * 4]); - plane /= length(plane.xyz()); - return plane; -} - -vec4 FrustumPlanes::top_frustum_plane(const mat4 &matrix) -{ - vec4 plane( - matrix[3 + 0 * 4] - matrix[1 + 0 * 4], - matrix[3 + 1 * 4] - matrix[1 + 1 * 4], - matrix[3 + 2 * 4] - matrix[1 + 2 * 4], - matrix[3 + 3 * 4] - matrix[1 + 3 * 4]); - plane /= length(plane.xyz()); - return plane; -} - -vec4 FrustumPlanes::bottom_frustum_plane(const mat4 &matrix) -{ - vec4 plane( - matrix[3 + 0 * 4] + matrix[1 + 0 * 4], - matrix[3 + 1 * 4] + matrix[1 + 1 * 4], - matrix[3 + 2 * 4] + matrix[1 + 2 * 4], - matrix[3 + 3 * 4] + matrix[1 + 3 * 4]); - plane /= length(plane.xyz()); - return plane; -} - -vec4 FrustumPlanes::near_frustum_plane(const mat4 &matrix) -{ - vec4 plane( - matrix[3 + 0 * 4] + matrix[2 + 0 * 4], - matrix[3 + 1 * 4] + matrix[2 + 1 * 4], - matrix[3 + 2 * 4] + matrix[2 + 2 * 4], - matrix[3 + 3 * 4] + matrix[2 + 3 * 4]); - plane /= length(plane.xyz()); - return plane; -} - -vec4 FrustumPlanes::far_frustum_plane(const mat4 &matrix) -{ - vec4 plane( - matrix[3 + 0 * 4] - matrix[2 + 0 * 4], - matrix[3 + 1 * 4] - matrix[2 + 1 * 4], - matrix[3 + 2 * 4] - matrix[2 + 2 * 4], - matrix[3 + 3 * 4] - matrix[2 + 3 * 4]); - plane /= length(plane.xyz()); - return plane; -} diff --git a/src/lightmap/collision.h b/src/lightmap/collision.h deleted file mode 100644 index 2852117..0000000 --- a/src/lightmap/collision.h +++ /dev/null @@ -1,207 +0,0 @@ -/* -** ZDRay collision -** Copyright (c) 2018 Magnus Norddahl -** -** This software is provided 'as-is', without any express or implied -** warranty. In no event will the authors be held liable for any damages -** arising from the use of this software. -** -** Permission is granted to anyone to use this software for any purpose, -** including commercial applications, and to alter it and redistribute it -** freely, subject to the following restrictions: -** -** 1. The origin of this software must not be misrepresented; you must not -** claim that you wrote the original software. If you use this software -** in a product, an acknowledgment in the product documentation would be -** appreciated but is not required. -** 2. Altered source versions must be plainly marked as such, and must not be -** misrepresented as being the original software. -** 3. This notice may not be removed or altered from any source distribution. -** -*/ - -#pragma once - -#include "math/mathlib.h" -#include -#include - -class SphereShape -{ -public: - SphereShape() { } - SphereShape(const vec3 ¢er, float radius) : center(center), radius(radius) { } - - vec3 center; - float radius = 0.0f; -}; - -struct TraceHit -{ - float fraction = 1.0f; - int triangle = -1; - float b = 0.0f; - float c = 0.0f; -}; - -class CollisionBBox : public BBox -{ -public: - CollisionBBox() = default; - - CollisionBBox(const vec3 &aabb_min, const vec3 &aabb_max) : BBox(aabb_min, aabb_max) - { - auto halfmin = aabb_min * 0.5f; - auto halfmax = aabb_max * 0.5f; - Center = halfmax + halfmin; - Extents = halfmax - halfmin; - } - - vec3 Center; - vec3 Extents; - float ssePadding = 0.0f; // Needed to safely load Extents directly into a sse register -}; - -class RayBBox -{ -public: - RayBBox(const vec3 &ray_start, const vec3 &ray_end) : start(ray_start), end(ray_end) - { - c = (ray_start + ray_end) * 0.5f; - w = ray_end - c; - v.x = std::abs(w.x); - v.y = std::abs(w.y); - v.z = std::abs(w.z); - } - - vec3 start, end; - vec3 c, w, v; - float ssePadding = 0.0f; // Needed to safely load v directly into a sse register -}; - -class TriangleMeshShape -{ -public: - TriangleMeshShape(const vec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements); - - int get_min_depth() const; - int get_max_depth() const; - float get_average_depth() const; - float get_balanced_depth() const; - - const CollisionBBox &get_bbox() const { return nodes[root].aabb; } - - static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, const vec3 &target); - - static bool find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2); - static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2); - static bool find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end); - - static std::vector find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2); - - static TraceHit find_first_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end); - - struct Node - { - Node() = default; - Node(const vec3 &aabb_min, const vec3 &aabb_max, int element_index) : aabb(aabb_min, aabb_max), element_index(element_index) { } - Node(const vec3 &aabb_min, const vec3 &aabb_max, int left, int right) : aabb(aabb_min, aabb_max), left(left), right(right) { } - - CollisionBBox aabb; - int left = -1; - int right = -1; - int element_index = -1; - }; - - const std::vector& get_nodes() const { return nodes; } - int get_root() const { return root; } - -private: - const vec3 *vertices = nullptr; - const int num_vertices = 0; - const unsigned int *elements = nullptr; - int num_elements = 0; - - std::vector nodes; - int root = -1; - - static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target); - - static bool find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b); - static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2, int a); - static bool find_any_hit(TriangleMeshShape *shape1, const RayBBox &ray, int a); - - static void find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2, int a, std::vector& hits); - - static void find_first_hit(TriangleMeshShape *shape1, const RayBBox &ray, int a, TraceHit *hit); - - inline static bool overlap_bv_ray(TriangleMeshShape *shape, const RayBBox &ray, int a); - inline static float intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a, float &barycentricB, float &barycentricC); - - inline static bool sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target); - inline static float sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target); - - inline static bool overlap_bv(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b); - inline static bool overlap_bv_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b); - inline static bool overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a); - inline static bool overlap_triangle_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b); - inline static bool overlap_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a); - - inline bool is_leaf(int node_index); - inline float volume(int node_index); - - int subdivide(int *triangles, int num_triangles, const vec3 *centroids, int *work_buffer); -}; - -class OrientedBBox -{ -public: - vec3 Center; - vec3 Extents; - vec3 axis_x; - vec3 axis_y; - vec3 axis_z; -}; - -class FrustumPlanes -{ -public: - FrustumPlanes(); - explicit FrustumPlanes(const mat4 &world_to_projection); - - vec4 planes[6]; - -private: - static vec4 left_frustum_plane(const mat4 &matrix); - static vec4 right_frustum_plane(const mat4 &matrix); - static vec4 top_frustum_plane(const mat4 &matrix); - static vec4 bottom_frustum_plane(const mat4 &matrix); - static vec4 near_frustum_plane(const mat4 &matrix); - static vec4 far_frustum_plane(const mat4 &matrix); -}; - -class IntersectionTest -{ -public: - enum Result - { - outside, - inside, - intersecting, - }; - - enum OverlapResult - { - disjoint, - overlap - }; - - static Result plane_aabb(const vec4 &plane, const BBox &aabb); - static Result plane_obb(const vec4 &plane, const OrientedBBox &obb); - static OverlapResult sphere(const vec3 ¢er1, float radius1, const vec3 ¢er2, float radius2); - static OverlapResult sphere_aabb(const vec3 ¢er, float radius, const BBox &aabb); - static OverlapResult aabb(const BBox &a, const BBox &b); - static Result frustum_aabb(const FrustumPlanes &frustum, const BBox &box); - static Result frustum_obb(const FrustumPlanes &frustum, const OrientedBBox &box); - static OverlapResult ray_aabb(const RayBBox &ray, const CollisionBBox &box); -}; diff --git a/src/lightmap/delauneytriangulator.cpp b/src/lightmap/delauneytriangulator.cpp deleted file mode 100644 index 1dd53cd..0000000 --- a/src/lightmap/delauneytriangulator.cpp +++ /dev/null @@ -1,250 +0,0 @@ - -#include "delauneytriangulator.h" - -void DelauneyTriangulator::triangulate() -{ - std::vector ordered_vertices = remove_duplicates(create_ordered_vertex_list()); - - Vertex super_A, super_B, super_C; - Triangle super_triangle; - super_triangle.A = &super_A; - super_triangle.B = &super_B; - super_triangle.C = &super_C; - calculate_supertriangle(ordered_vertices, super_triangle); - - triangles = perform_delauney_triangulation(ordered_vertices, super_triangle); -} - -std::vector DelauneyTriangulator::create_ordered_vertex_list() -{ - if (vertices.empty()) - return {}; - - std::vector ordered_vertices; - - size_t num_vertices = vertices.size(); - for (size_t index_vertices = 0; index_vertices < num_vertices; index_vertices++) - { - ordered_vertices.push_back(&vertices[index_vertices]); - } - - std::sort(ordered_vertices.begin(), ordered_vertices.end(), [](Vertex* a, Vertex* b) { return a->x == b->x ? a->y < b->y : a->x < b->x; }); - - return ordered_vertices; -} - -std::vector DelauneyTriangulator::remove_duplicates(const std::vector& ordered_vertices) -{ - // Link duplicates and remove them from the ordered list: - - std::vector filtered_vertices; - Vertex* prev = nullptr; - for (Vertex* v : ordered_vertices) - { - v->next = nullptr; // clear for all vertices just in case triangulate is ever called twice - - if (prev && prev->x == v->x && prev->y == v->y) - { - prev->next = v; - } - else - { - filtered_vertices.push_back(v); - } - - prev = v; - } - return filtered_vertices; -} - -void DelauneyTriangulator::calculate_supertriangle(std::vector& vertices, Triangle& super_triangle) -{ - // Find min and max values: - - size_t num_vertices = vertices.size(); - - float min_x = 0.0f; - float max_x = 0.0f; - float min_y = 0.0f; - float max_y = 0.0f; - if (num_vertices > 0) - { - min_x = vertices[0]->x; - max_x = vertices[0]->x; - min_y = vertices[0]->y; - max_y = vertices[0]->y; - } - - for (size_t index_vertices = 1; index_vertices < num_vertices; index_vertices++) - { - Vertex* cur_vertex = vertices[index_vertices]; - - min_x = std::min(min_x, cur_vertex->x); - max_x = std::max(max_x, cur_vertex->x); - min_y = std::min(min_y, cur_vertex->y); - max_y = std::max(max_y, cur_vertex->y); - } - - // Setup super triangle based on min/max values: - - float dx = max_x - min_x; - float dy = max_y - min_y; - float dmax = (dx > dy) ? dx : dy; - float xmid = (max_x + min_x) * 0.5f; - float ymid = (max_y + min_y) * 0.5f; - - super_triangle.A->x = xmid - 20.0f * dmax; - super_triangle.A->y = ymid - dmax; - super_triangle.A->data = nullptr; - - super_triangle.B->x = xmid; - super_triangle.B->y = ymid + 20.0f * dmax; - super_triangle.B->data = nullptr; - - super_triangle.C->x = xmid + 20.0f * dmax; - super_triangle.C->y = ymid - dmax; - super_triangle.C->data = nullptr; - - calc_cirumcenter(super_triangle); -} - -std::vector DelauneyTriangulator::perform_delauney_triangulation(const std::vector& vertices, const Triangle& super_triangle) -{ - std::vector triangles; - - // add supertriangle vertices to the end of the vertex list - triangles.push_back(super_triangle); - - std::vector edges; - - // for each sample point in the vertex list: - size_t num_vertices = vertices.size(); - for (size_t index_vertices = 0; index_vertices < num_vertices; index_vertices++) - { - Vertex* insertion_point = vertices[index_vertices]; - - edges.clear(); - - // For each triangle currently in the triangle list - std::vector::size_type index_triangles, num_triangles; - num_triangles = triangles.size(); - for (index_triangles = 0; index_triangles < num_triangles; index_triangles++) - { - Triangle& cur_triangle = triangles[index_triangles]; - - // Check if the point lies in the triangle circumcircle: - float dist_x = insertion_point->x - cur_triangle.circumcenter_x; - float dist_y = insertion_point->y - cur_triangle.circumcenter_y; - float dist2 = dist_x * dist_x + dist_y * dist_y; - if (dist2 < cur_triangle.radius2) - { - // Add triangle edges to edge buffer: - edges.push_back(Triangle_Edge(cur_triangle.A, cur_triangle.B)); - edges.push_back(Triangle_Edge(cur_triangle.B, cur_triangle.C)); - edges.push_back(Triangle_Edge(cur_triangle.C, cur_triangle.A)); - - // Remove triange from triangle list: - triangles.erase(triangles.begin() + index_triangles); - index_triangles--; - num_triangles--; - } - } - - // Delete all doubly specified edges from the edge buffer. This leaves the edges of the enclosing polygon only - int64_t index_edges1, index_edges2, num_edges; // intentionally integer to allow index to be negative when deleting index. - num_edges = (int64_t)edges.size(); - for (index_edges1 = 0; index_edges1 < num_edges; index_edges1++) - { - Triangle_Edge& edge1 = edges[index_edges1]; - for (index_edges2 = 0/*index_edges1+1*/; index_edges2 < num_edges; index_edges2++) - { - if (index_edges1 == index_edges2) continue; - Triangle_Edge& edge2 = edges[index_edges2]; - if ((edge1.first == edge2.first && edge1.second == edge2.second) || - (edge1.second == edge2.first && edge1.first == edge2.second)) - { - // Same edges, delete both: - if (index_edges1 < index_edges2) - { - edges.erase(edges.begin() + index_edges2); - edges.erase(edges.begin() + index_edges1); - } - else - { - edges.erase(edges.begin() + index_edges1); - edges.erase(edges.begin() + index_edges2); - } - num_edges -= 2; - index_edges1--; - break; - } - } - } - - // add to the triangle list all triangles formed between the point and the edges of the enclosing polygon - for (index_edges1 = 0; index_edges1 < num_edges; index_edges1++) - { - Triangle triangle; - triangle.A = edges[index_edges1].first; - triangle.B = edges[index_edges1].second; - triangle.C = insertion_point; - calc_cirumcenter(triangle); - triangles.push_back(triangle); - } - } - - // remove any triangles from the triangle list that use the supertriangle vertices - size_t num_triangles = triangles.size(); - for (size_t index_triangles = 0; index_triangles < num_triangles; index_triangles++) - { - Triangle& cur_triangle = triangles[index_triangles]; - - if ( - cur_triangle.A == super_triangle.A || - cur_triangle.A == super_triangle.B || - cur_triangle.A == super_triangle.C || - cur_triangle.B == super_triangle.A || - cur_triangle.B == super_triangle.B || - cur_triangle.B == super_triangle.C || - cur_triangle.C == super_triangle.A || - cur_triangle.C == super_triangle.B || - cur_triangle.C == super_triangle.C) - { - // triangle shares one or more points with supertriangle, remove it: - triangles.erase(triangles.begin() + index_triangles); - index_triangles--; - num_triangles--; - } - } - - return triangles; -} - -void DelauneyTriangulator::calc_cirumcenter(Triangle& triangle) -{ - float a_0 = triangle.A->x; - float a_1 = triangle.A->y; - float b_0 = triangle.B->x; - float b_1 = triangle.B->y; - float c_0 = triangle.C->x; - float c_1 = triangle.C->y; - - float A = b_0 - a_0; - float B = b_1 - a_1; - float C = c_0 - a_0; - float D = c_1 - a_1; - - float E = A * (a_0 + b_0) + B * (a_1 + b_1); - float F = C * (a_0 + c_0) + D * (a_1 + c_1); - - float G = 2.0f * (A * (c_1 - b_1) - B * (c_0 - b_0)); - - float p_0 = (D * E - B * F) / G; - float p_1 = (A * F - C * E) / G; - - triangle.circumcenter_x = p_0; - triangle.circumcenter_y = p_1; - float radius_x = triangle.A->x - triangle.circumcenter_x; - float radius_y = triangle.A->y - triangle.circumcenter_y; - triangle.radius2 = radius_x * radius_x + radius_y * radius_y; -} diff --git a/src/lightmap/delauneytriangulator.h b/src/lightmap/delauneytriangulator.h deleted file mode 100644 index 3deb6d3..0000000 --- a/src/lightmap/delauneytriangulator.h +++ /dev/null @@ -1,45 +0,0 @@ - -#pragma once - -#include -#include - -// Bowyer-Watson delauney triangulator (http://paulbourke.net/papers/triangulate/) -class DelauneyTriangulator -{ -public: - struct Vertex - { - Vertex() = default; - Vertex(float x, float y, void* data = nullptr) : x(x), y(y), data(data) { } - - float x = 0.0f; - float y = 0.0f; - void* data = nullptr; - Vertex* next = nullptr; - }; - - struct Triangle - { - Vertex* A; - Vertex* B; - Vertex* C; - float circumcenter_x; - float circumcenter_y; - float radius2; - }; - - typedef std::pair Triangle_Edge; - - std::vector vertices; - std::vector triangles; - - void triangulate(); - -private: - std::vector create_ordered_vertex_list(); - static std::vector remove_duplicates(const std::vector& ordered_vertices); - static void calculate_supertriangle(std::vector& vertices, Triangle& super_triangle); - static void calc_cirumcenter(Triangle& triangle); - static std::vector perform_delauney_triangulation(const std::vector& vertices, const Triangle& super_triangle); -}; diff --git a/src/lightmap/glsl_frag.h b/src/lightmap/glsl_frag.h deleted file mode 100644 index 5bb2ac5..0000000 --- a/src/lightmap/glsl_frag.h +++ /dev/null @@ -1,566 +0,0 @@ -static const char* glsl_frag = R"glsl( - -#if defined(USE_RAYQUERY) -layout(set = 1, binding = 0) uniform accelerationStructureEXT acc; -#else -struct CollisionNode -{ - vec3 center; - float padding1; - vec3 extents; - float padding2; - int left; - int right; - int element_index; - int padding3; -}; -layout(std430, set = 1, binding = 0) buffer NodeBuffer -{ - int nodesRoot; - int nodebufferPadding1; - int nodebufferPadding2; - int nodebufferPadding3; - CollisionNode nodes[]; -}; -layout(std430, set = 1, binding = 1) buffer VertexBuffer { vec4 vertices[]; }; -layout(std430, set = 1, binding = 2) buffer ElementBuffer { int elements[]; }; -#endif - -layout(set = 0, binding = 0) uniform Uniforms -{ - vec3 SunDir; - float Padding1; - vec3 SunColor; - float SunIntensity; -}; - -struct SurfaceInfo -{ - vec3 Normal; - float Sky; - float SamplingDistance; - uint PortalIndex; - float Padding1, Padding2; -}; - -struct PortalInfo -{ - mat4 Transformation; -}; - -struct LightInfo -{ - vec3 Origin; - float Padding0; - vec3 RelativeOrigin; - float Padding1; - float Radius; - float Intensity; - float InnerAngleCos; - float OuterAngleCos; - vec3 SpotDir; - float Padding2; - vec3 Color; - float Padding3; -}; - -layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; }; -layout(set = 0, binding = 2) buffer SurfaceBuffer { SurfaceInfo surfaces[]; }; -layout(set = 0, binding = 3) buffer LightBuffer { LightInfo lights[]; }; -layout(set = 0, binding = 4) buffer PortalBuffer { PortalInfo portals[]; }; - -layout(push_constant) uniform PushConstants -{ - uint LightStart; - uint LightEnd; - int SurfaceIndex; - int PushPadding1; - vec3 LightmapOrigin; - float PushPadding2; - vec3 LightmapStepX; - float PushPadding3; - vec3 LightmapStepY; - float PushPadding4; -}; - -layout(location = 0) centroid in vec3 worldpos; -layout(location = 0) out vec4 fragcolor; - -vec3 TraceSunLight(vec3 origin); -vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light); -float TraceAmbientOcclusion(vec3 origin, vec3 normal); -vec2 Hammersley(uint i, uint N); -float RadicalInverse_VdC(uint bits); - -bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax); -bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax); -int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax); -int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t); - -void main() -{ - vec3 normal = surfaces[SurfaceIndex].Normal; - vec3 origin = worldpos + normal * 0.1; - - vec3 incoming = TraceSunLight(origin); - - for (uint j = LightStart; j < LightEnd; j++) - { - incoming += TraceLight(origin, normal, lights[j]); - } - -#if defined(USE_RAYQUERY) // The non-rtx version of TraceFirstHitTriangle is too slow to do AO without the shader getting killed ;( - incoming.rgb *= TraceAmbientOcclusion(origin, normal); -#endif - - fragcolor = vec4(incoming, 1.0); -} - -vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light) -{ - const float minDistance = 0.01; - vec3 incoming = vec3(0.0); - float dist = distance(light.RelativeOrigin, origin); - if (dist > minDistance && dist < light.Radius) - { - vec3 dir = normalize(light.RelativeOrigin - origin); - - float distAttenuation = max(1.0 - (dist / light.Radius), 0.0); - float angleAttenuation = 1.0f; - if (SurfaceIndex >= 0) - { - angleAttenuation = max(dot(normal, dir), 0.0); - } - float spotAttenuation = 1.0; - if (light.OuterAngleCos > -1.0) - { - float cosDir = dot(dir, light.SpotDir); - spotAttenuation = smoothstep(light.OuterAngleCos, light.InnerAngleCos, cosDir); - spotAttenuation = max(spotAttenuation, 0.0); - } - - float attenuation = distAttenuation * angleAttenuation * spotAttenuation; - if (attenuation > 0.0) - { - if(TracePoint(origin, light.Origin, minDistance, dir, dist)) - { - incoming.rgb += light.Color * (attenuation * light.Intensity); - } - } - } - - return incoming; -} - -vec3 TraceSunLight(vec3 origin) -{ - const float minDistance = 0.01; - vec3 incoming = vec3(0.0); - const float dist = 32768.0; - - int primitiveID = TraceFirstHitTriangle(origin, minDistance, SunDir, dist); - if (primitiveID != -1) - { - SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; - incoming.rgb += SunColor * SunIntensity * surface.Sky; - } - return incoming; -} - -float TraceAmbientOcclusion(vec3 origin, vec3 normal) -{ - const float minDistance = 0.05; - const float aoDistance = 100; - const int SampleCount = 2048; - - vec3 N = normal; - vec3 up = abs(N.x) < abs(N.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0); - vec3 tangent = normalize(cross(up, N)); - vec3 bitangent = cross(N, tangent); - - float ambience = 0.0f; - for (uint i = 0; i < SampleCount; i++) - { - vec2 Xi = Hammersley(i, SampleCount); - vec3 H = normalize(vec3(Xi.x * 2.0f - 1.0f, Xi.y * 2.0f - 1.0f, 1.5 - length(Xi))); - vec3 L = H.x * tangent + H.y * bitangent + H.z * N; - - float hitDistance; - int primitiveID = TraceFirstHitTriangleT(origin, minDistance, L, aoDistance, hitDistance); - if (primitiveID != -1) - { - SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; - if (surface.Sky == 0.0) - { - ambience += clamp(hitDistance / aoDistance, 0.0, 1.0); - } - } - else - { - ambience += 1.0; - } - } - return ambience / float(SampleCount); -} - -vec2 Hammersley(uint i, uint N) -{ - return vec2(float(i) / float(N), RadicalInverse_VdC(i)); -} - -float RadicalInverse_VdC(uint bits) -{ - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10f; // / 0x100000000 -} - -#if defined(USE_RAYQUERY) - -int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float t) -{ - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax); - - while(rayQueryProceedEXT(rayQuery)) - { - if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCommittedIntersectionTriangleEXT) - { - rayQueryConfirmIntersectionEXT(rayQuery); - } - } - - if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT) - { - t = rayQueryGetIntersectionTEXT(rayQuery, true); - return rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true); - } - else - { - t = tmax; - return -1; - } -} - -/* -bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) -{ - rayQueryEXT rayQuery; - rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax); - while(rayQueryProceedEXT(rayQuery)) { } - return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT; -} -*/ - -#else - -struct RayBBox -{ - vec3 start, end; - vec3 c, w, v; -}; - -RayBBox create_ray(vec3 ray_start, vec3 ray_end) -{ - RayBBox ray; - ray.start = ray_start; - ray.end = ray_end; - ray.c = (ray_start + ray_end) * 0.5; - ray.w = ray_end - ray.c; - ray.v = abs(ray.w); - return ray; -} - -bool overlap_bv_ray(RayBBox ray, int a) -{ - vec3 v = ray.v; - vec3 w = ray.w; - vec3 h = nodes[a].extents; - vec3 c = ray.c - nodes[a].center; - - if (abs(c.x) > v.x + h.x || - abs(c.y) > v.y + h.y || - abs(c.z) > v.z + h.z) - { - return false; - } - - if (abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || - abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || - abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) - { - return false; - } - - return true; -} - -#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0 - -float intersect_triangle_ray(RayBBox ray, int a, out float barycentricB, out float barycentricC) -{ - int start_element = nodes[a].element_index; - - vec3 p[3]; - p[0] = vertices[elements[start_element]].xyz; - p[1] = vertices[elements[start_element + 1]].xyz; - p[2] = vertices[elements[start_element + 2]].xyz; - - // Moeller-Trumbore ray-triangle intersection algorithm: - - vec3 D = ray.end - ray.start; - - // Find vectors for two edges sharing p[0] - vec3 e1 = p[1] - p[0]; - vec3 e2 = p[2] - p[0]; - - // Begin calculating determinant - also used to calculate u parameter - vec3 P = cross(D, e2); - float det = dot(e1, P); - - // Backface check - //if (det < 0.0f) - // return 1.0f; - - // If determinant is near zero, ray lies in plane of triangle - if (det > -FLT_EPSILON && det < FLT_EPSILON) - return 1.0f; - - float inv_det = 1.0f / det; - - // Calculate distance from p[0] to ray origin - vec3 T = ray.start - p[0]; - - // Calculate u parameter and test bound - float u = dot(T, P) * inv_det; - - // Check if the intersection lies outside of the triangle - if (u < 0.f || u > 1.f) - return 1.0f; - - // Prepare to test v parameter - vec3 Q = cross(T, e1); - - // Calculate V parameter and test bound - float v = dot(D, Q) * inv_det; - - // The intersection lies outside of the triangle - if (v < 0.f || u + v > 1.f) - return 1.0f; - - float t = dot(e2, Q) * inv_det; - if (t <= FLT_EPSILON) - return 1.0f; - - // Return hit location on triangle in barycentric coordinates - barycentricB = u; - barycentricC = v; - - return t; -} - -bool is_leaf(int node_index) -{ - return nodes[node_index].element_index != -1; -} - -/* -bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) -{ - if (tmax <= 0.0f) - return false; - - RayBBox ray = create_ray(origin, origin + dir * tmax); - tmin /= tmax; - - int stack[64]; - int stackIndex = 0; - stack[stackIndex++] = nodesRoot; - do - { - int a = stack[--stackIndex]; - if (overlap_bv_ray(ray, a)) - { - if (is_leaf(a)) - { - float baryB, baryC; - float t = intersect_triangle_ray(ray, a, baryB, baryC); - if (t >= tmin && t < 1.0) - { - return true; - } - } - else - { - stack[stackIndex++] = nodes[a].right; - stack[stackIndex++] = nodes[a].left; - } - } - } while (stackIndex > 0); - return false; -} -*/ - -struct TraceHit -{ - float fraction; - int triangle; - float b; - float c; -}; - -TraceHit find_first_hit(RayBBox ray) -{ - TraceHit hit; - hit.fraction = 1.0; - hit.triangle = -1; - hit.b = 0.0; - hit.c = 0.0; - - int stack[64]; - int stackIndex = 0; - stack[stackIndex++] = nodesRoot; - do - { - int a = stack[--stackIndex]; - if (overlap_bv_ray(ray, a)) - { - if (is_leaf(a)) - { - float baryB, baryC; - float t = intersect_triangle_ray(ray, a, baryB, baryC); - if (t < hit.fraction) - { - hit.fraction = t; - hit.triangle = nodes[a].element_index / 3; - hit.b = baryB; - hit.c = baryC; - } - } - else - { - stack[stackIndex++] = nodes[a].right; - stack[stackIndex++] = nodes[a].left; - } - } - } while (stackIndex > 0); - return hit; -} - -int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float tparam) -{ - // Perform segmented tracing to keep the ray AABB box smaller - vec3 ray_start = origin; - vec3 ray_end = origin + dir * tmax; - vec3 ray_dir = dir; - float tracedist = tmax; - float segmentlen = max(200.0, tracedist / 20.0); - for (float t = 0.0; t < tracedist; t += segmentlen) - { - float segstart = t; - float segend = min(t + segmentlen, tracedist); - - RayBBox ray = create_ray(ray_start + ray_dir * segstart, ray_start + ray_dir * segend); - TraceHit hit = find_first_hit(ray); - if (hit.fraction < 1.0) - { - tparam = hit.fraction = segstart * (1.0 - hit.fraction) + segend * hit.fraction; - return hit.triangle; - } - } - - tparam = tracedist; - return -1; -} - - -#endif - -int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t) -{ - int primitiveID; - while(true) - { - primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t); - - if(primitiveID < 0) - { - break; - } - - SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; - - if(surface.PortalIndex == 0) - { - break; - } - - // Portal was hit: Apply transformation onto the ray - mat4 transformationMatrix = portals[surface.PortalIndex].Transformation; - - origin = (transformationMatrix * vec4(origin + dir * t, 1.0)).xyz; - dir = (transformationMatrix * vec4(dir, 0.0)).xyz; - tmax -= t; - } - return primitiveID; -} - -int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax) -{ - float t; - return TraceFirstHitTriangleT(origin, tmin, dir, tmax, t); -} - -bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax) -{ - return TraceFirstHitTriangle(origin, tmin, dir, tmax) >= 0; -} - -bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax) -{ - int primitiveID; - float t; - while(true) - { - t = tmax; - primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t); - - origin += dir * t; - tmax -= t; - - if(primitiveID < 0) - { - // We didn't hit anything - break; - } - - SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]]; - - if(surface.PortalIndex == 0) - { - break; - } - - if(dot(surface.Normal, dir) >= 0.0) - { - continue; - } - - mat4 transformationMatrix = portals[surface.PortalIndex].Transformation; - origin = (transformationMatrix * vec4(origin, 1.0)).xyz; - dir = (transformationMatrix * vec4(dir, 0.0)).xyz; - -#if defined(USE_RAYQUERY) -#else - origin += dir * tmin; - tmax -= tmin; -#endif - } - - return distance(origin, target) <= 1.0; -} - -)glsl"; diff --git a/src/lightmap/glsl_frag_resolve.h b/src/lightmap/glsl_frag_resolve.h deleted file mode 100644 index 33c9d12..0000000 --- a/src/lightmap/glsl_frag_resolve.h +++ /dev/null @@ -1,49 +0,0 @@ -static const char* glsl_frag_resolve = R"glsl( - -layout(set = 0, binding = 0) uniform sampler2DMS tex; - -layout(location = 0) in vec3 worldpos; -layout(location = 0) out vec4 fragcolor; - -vec4 samplePixel(ivec2 pos, int count) -{ - vec4 c = vec4(0.0); - for (int i = 0; i < count; i++) - { - c += texelFetch(tex, pos, i); - } - if (c.a > 0.0) - c /= c.a; - return c; -} - -void main() -{ - int count = textureSamples(tex); - ivec2 size = textureSize(tex); - ivec2 pos = ivec2(gl_FragCoord.xy); - - vec4 c = samplePixel(pos, count); - if (c.a == 0.0) - { - for (int y = -1; y <= 1; y++) - { - for (int x = -1; x <= 1; x++) - { - if (x != 0 || y != 0) - { - ivec2 pos2; - pos2.x = clamp(pos.x + x, 0, size.x - 1); - pos2.y = clamp(pos.y + y, 0, size.y - 1); - c += samplePixel(pos2, count); - } - } - } - if (c.a > 0.0) - c /= c.a; - } - - fragcolor = c; -} - -)glsl"; diff --git a/src/lightmap/glsl_vert.h b/src/lightmap/glsl_vert.h deleted file mode 100644 index b483b10..0000000 --- a/src/lightmap/glsl_vert.h +++ /dev/null @@ -1,26 +0,0 @@ -static const char* glsl_vert = R"glsl( - -layout(push_constant) uniform PushConstants -{ - uint LightStart; - uint LightEnd; - int SurfaceIndex; - int PushPadding1; - vec3 LightmapOrigin; - float PushPadding2; - vec3 LightmapStepX; - float PushPadding3; - vec3 LightmapStepY; - float PushPadding4; -}; - -layout(location = 0) in vec2 aPosition; -layout(location = 0) out vec3 worldpos; - -void main() -{ - worldpos = LightmapOrigin + LightmapStepX * aPosition.x + LightmapStepY * aPosition.y; - gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0); -} - -)glsl"; diff --git a/src/lightmap/gpuraytracer.cpp b/src/lightmap/gpuraytracer.cpp deleted file mode 100644 index 2954661..0000000 --- a/src/lightmap/gpuraytracer.cpp +++ /dev/null @@ -1,1117 +0,0 @@ - -#include "math/mathlib.h" -#include "levelmesh.h" -#include "level/level.h" -#include "gpuraytracer.h" -#include "framework/binfile.h" -#include "framework/templates.h" -#include "framework/halffloat.h" -#include -#include -#include "renderdoc_app.h" -#include "stacktrace.h" -#include -#include -#include -#include -#include -#include -#include -#include "glsl_frag.h" -#include "glsl_frag_resolve.h" -#include "glsl_vert.h" - -extern bool VKDebug; -extern bool NoRtx; - -#ifndef _WIN32 -#include -#endif - -static RENDERDOC_API_1_4_2* rdoc_api; - -GPURaytracer::GPURaytracer() -{ - if(!rdoc_api) - LoadRenderDoc(); - - auto instance = VulkanInstanceBuilder() - .DebugLayer(VKDebug) - .Create(); - - device = VulkanDeviceBuilder() - .OptionalRayQuery() - .Create(instance); - - useRayQuery = !NoRtx && device->PhysicalDevice.Features.RayQuery.rayQuery; - - PrintVulkanInfo(); -} - -GPURaytracer::~GPURaytracer() -{ -} - -void GPURaytracer::Raytrace(LevelMesh* level) -{ - if (rdoc_api) rdoc_api->StartFrameCapture(nullptr, nullptr); - - mesh = level; - - printf(" Building Vulkan acceleration structures\n"); - - CreateVulkanObjects(); - - try - { - printf(" Ray tracing in progress\n"); - printf(" ["); - - CreateAtlasImages(); - -#ifdef WIN32 - LARGE_INTEGER s; - QueryPerformanceCounter(&s); -#endif - - BeginCommands(); - UploadUniforms(); - - for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++) - { - RenderAtlasImage(pageIndex); - } - - for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++) - { - ResolveAtlasImage(pageIndex); - } - - FinishCommands(); - -#ifdef WIN32 - LARGE_INTEGER e, f; - QueryPerformanceCounter(&e); - QueryPerformanceFrequency(&f); - printf("]\n"); - printf(" GPU ray tracing time was %.3f seconds.\n", double(e.QuadPart - s.QuadPart) / double(f.QuadPart)); -#endif - - for (size_t pageIndex = 0; pageIndex < atlasImages.size(); pageIndex++) - { - DownloadAtlasImage(pageIndex); - } - - printf(" Ray trace complete\n"); - } - catch (...) - { - printf("]\n"); - throw; - } - - if (rdoc_api) rdoc_api->EndFrameCapture(nullptr, nullptr); -} - -void GPURaytracer::RenderAtlasImage(size_t pageIndex) -{ - LightmapImage& img = atlasImages[pageIndex]; - - // Begin with clear - { - RenderPassBegin() - .RenderPass(raytrace.renderPassBegin.get()) - .RenderArea(0, 0, atlasImageSize, atlasImageSize) - .Framebuffer(img.raytrace.Framebuffer.get()) - .AddClearColor(0.0f, 0.0f, 0.0f, 0.0f) - .Execute(cmdbuffer.get()); - - VkDeviceSize offset = 0; - cmdbuffer->bindVertexBuffers(0, 1, &sceneVertexBuffer->buffer, &offset); - cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline.get()); - cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get()); - cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get()); - } - - for (size_t i = 0; i < mesh->surfaces.size(); i++) - { - Surface* targetSurface = mesh->surfaces[i].get(); - if (targetSurface->atlasPageIndex != pageIndex) - continue; - - VkViewport viewport = {}; - viewport.maxDepth = 1; - viewport.x = (float)targetSurface->atlasX - 1; - viewport.y = (float)targetSurface->atlasY - 1; - viewport.width = (float)(targetSurface->texWidth + 2); - viewport.height = (float)(targetSurface->texHeight + 2); - cmdbuffer->setViewport(0, 1, &viewport); - - // Paint all surfaces part of the smoothing group into the surface - for (Surface* surface : mesh->smoothingGroups[targetSurface->smoothingGroupIndex].surfaces) - { - vec2 minUV = ToUV(surface->bounds.min, targetSurface); - vec2 maxUV = ToUV(surface->bounds.max, targetSurface); - if (surface != targetSurface && (maxUV.x < 0.0f || maxUV.y < 0.0f || minUV.x > 1.0f || minUV.y > 1.0f)) - continue; // Bounding box not visible - - int firstLight = sceneLightPos; - int firstVertex = sceneVertexPos; - int lightCount = (int)surface->LightList.size(); - int vertexCount = (int)surface->verts.size(); - if (sceneLightPos + lightCount > SceneLightBufferSize || sceneVertexPos + vertexCount > SceneVertexBufferSize) - { - printf("."); - - // Flush scene buffers - FinishCommands(); - sceneLightPos = 0; - sceneVertexPos = 0; - firstLight = 0; - firstVertex = 0; - BeginCommands(); - - // Begin without clear - RenderPassBegin() - .RenderPass(raytrace.renderPassContinue.get()) - .RenderArea(0, 0, atlasImageSize, atlasImageSize) - .Framebuffer(img.raytrace.Framebuffer.get()) - .Execute(cmdbuffer.get()); - - printf("."); - - if (sceneLightPos + lightCount > SceneLightBufferSize) - { - throw std::runtime_error("SceneLightBuffer is too small!"); - } - else if (sceneVertexPos + vertexCount > SceneVertexBufferSize) - { - throw std::runtime_error("SceneVertexBuffer is too small!"); - } - } - sceneLightPos += lightCount; - sceneVertexPos += vertexCount; - - LightInfo* lightinfo = &sceneLights[firstLight]; - for (ThingLight* light : surface->LightList) - { - lightinfo->Origin = light->LightOrigin(); - lightinfo->RelativeOrigin = light->LightRelativeOrigin(); - lightinfo->Radius = light->LightRadius(); - lightinfo->Intensity = light->intensity; - lightinfo->InnerAngleCos = light->innerAngleCos; - lightinfo->OuterAngleCos = light->outerAngleCos; - lightinfo->SpotDir = light->SpotDir(); - lightinfo->Color = light->rgb; - lightinfo++; - } - - PushConstants pc; - pc.LightStart = firstLight; - pc.LightEnd = firstLight + lightCount; - pc.SurfaceIndex = (int32_t)i; - pc.LightmapOrigin = targetSurface->worldOrigin - targetSurface->worldStepX - targetSurface->worldStepY; - pc.LightmapStepX = targetSurface->worldStepX * viewport.width; - pc.LightmapStepY = targetSurface->worldStepY * viewport.height; - cmdbuffer->pushConstants(raytrace.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants), &pc); - - SceneVertex* vertex = &sceneVertices[firstVertex]; - - if (surface->type == ST_FLOOR || surface->type == ST_CEILING) - { - for (int idx = 0; idx < vertexCount; idx++) - { - (vertex++)->Position = ToUV(surface->verts[idx], targetSurface); - } - } - else - { - (vertex++)->Position = ToUV(surface->verts[0], targetSurface); - (vertex++)->Position = ToUV(surface->verts[2], targetSurface); - (vertex++)->Position = ToUV(surface->verts[3], targetSurface); - (vertex++)->Position = ToUV(surface->verts[1], targetSurface); - } - - cmdbuffer->draw(vertexCount, 1, firstVertex, 0); - } - } - - cmdbuffer->endRenderPass(); -} - -void GPURaytracer::CreateAtlasImages() -{ - const int spacing = 3; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles - RectPacker packer(atlasImageSize, atlasImageSize, RectPacker::Spacing(spacing)); - - for (size_t i = 0; i < mesh->surfaces.size(); i++) - { - Surface* surface = mesh->surfaces[i].get(); - - auto result = packer.insert(surface->texWidth + 2, surface->texHeight + 2); - surface->atlasX = result.pos.x + 1; - surface->atlasY = result.pos.y + 1; - surface->atlasPageIndex = (int)result.pageIndex; - } - - for (size_t pageIndex = 0; pageIndex < packer.getNumPages(); pageIndex++) - { - atlasImages.push_back(CreateImage(atlasImageSize, atlasImageSize)); - } -} - -void GPURaytracer::UploadUniforms() -{ - Uniforms uniforms = {}; - uniforms.SunDir = mesh->map->GetSunDirection(); - uniforms.SunColor = mesh->map->GetSunColor(); - uniforms.SunIntensity = 1.0f; - - mappedUniforms = (uint8_t*)uniformTransferBuffer->Map(0, uniformStructs * uniformStructStride); - *reinterpret_cast(mappedUniforms + uniformStructStride * uniformsIndex) = uniforms; - uniformTransferBuffer->Unmap(); - - cmdbuffer->copyBuffer(uniformTransferBuffer.get(), uniformBuffer.get()); - PipelineBarrier() - .AddBuffer(uniformBuffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); -} - -void GPURaytracer::ResolveAtlasImage(size_t i) -{ - LightmapImage& img = atlasImages[i]; - - PipelineBarrier() - .AddImage(img.raytrace.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - RenderPassBegin() - .RenderPass(resolve.renderPass.get()) - .RenderArea(0, 0, atlasImageSize, atlasImageSize) - .Framebuffer(img.resolve.Framebuffer.get()) - .Execute(cmdbuffer.get()); - - VkDeviceSize offset = 0; - cmdbuffer->bindVertexBuffers(0, 1, &sceneVertexBuffer->buffer, &offset); - cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipeline.get()); - - auto descriptorSet = resolve.descriptorPool->allocate(resolve.descriptorSetLayout.get()); - descriptorSet->SetDebugName("resolve.descriptorSet"); - WriteDescriptors() - .AddCombinedImageSampler(descriptorSet.get(), 0, img.raytrace.View.get(), resolve.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - .Execute(device.get()); - cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, resolve.pipelineLayout.get(), 0, descriptorSet.get()); - resolve.descriptorSets.push_back(std::move(descriptorSet)); - - VkViewport viewport = {}; - viewport.maxDepth = 1; - viewport.width = (float)atlasImageSize; - viewport.height = (float)atlasImageSize; - cmdbuffer->setViewport(0, 1, &viewport); - - PushConstants pc; - pc.LightStart = 0; - pc.LightEnd = 0; - pc.SurfaceIndex = 0; - pc.LightmapOrigin = vec3(0.0f); - pc.LightmapStepX = vec3(0.0f); - pc.LightmapStepY = vec3(0.0f); - cmdbuffer->pushConstants(resolve.pipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants), &pc); - - int firstVertex = sceneVertexPos; - int vertexCount = 4; - sceneVertexPos += vertexCount; - SceneVertex* vertex = &sceneVertices[firstVertex]; - vertex[0].Position = vec2(0.0f, 0.0f); - vertex[1].Position = vec2(1.0f, 0.0f); - vertex[2].Position = vec2(1.0f, 1.0f); - vertex[3].Position = vec2(0.0f, 1.0f); - cmdbuffer->draw(vertexCount, 1, firstVertex, 0); - - cmdbuffer->endRenderPass(); - - PipelineBarrier() - .AddImage(img.resolve.Image.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - - VkBufferImageCopy region = {}; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - region.imageExtent.width = atlasImageSize; - region.imageExtent.height = atlasImageSize; - region.imageExtent.depth = 1; - cmdbuffer->copyImageToBuffer(img.resolve.Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, img.Transfer->buffer, 1, ®ion); -} - -void GPURaytracer::DownloadAtlasImage(size_t pageIndex) -{ - struct hvec4 - { - unsigned short x, y, z, w; - vec3 xyz() { return vec3(halfToFloat(x), halfToFloat(y), halfToFloat(z)); } - }; - - hvec4* pixels = (hvec4*)atlasImages[pageIndex].Transfer->Map(0, atlasImageSize * atlasImageSize * sizeof(hvec4)); - - for (size_t i = 0; i < mesh->surfaces.size(); i++) - { - Surface* surface = mesh->surfaces[i].get(); - if (surface->atlasPageIndex != pageIndex) - continue; - - int atlasX = surface->atlasX; - int atlasY = surface->atlasY; - int sampleWidth = surface->texWidth; - int sampleHeight = surface->texHeight; - - for (int y = 0; y < sampleHeight; y++) - { - vec3* dest = &surface->texPixels[y * sampleWidth]; - hvec4* src = &pixels[atlasX + (atlasY + y) * atlasImageSize]; - for (int x = 0; x < sampleWidth; x++) - { - dest[x] = src[x].xyz(); - } - } - } - atlasImages[pageIndex].Transfer->Unmap(); -} - -vec2 GPURaytracer::ToUV(const vec3& vert, const Surface* targetSurface) -{ - vec3 localPos = vert - targetSurface->translateWorldToLocal; - float u = (1.0f + dot(localPos, targetSurface->projLocalToU)) / (targetSurface->texWidth + 2); - float v = (1.0f + dot(localPos, targetSurface->projLocalToV)) / (targetSurface->texHeight + 2); - return vec2(u, v); -} - -void GPURaytracer::CreateVulkanObjects() -{ - submitFence = std::make_unique(device.get()); - cmdpool = std::make_unique(device.get(), device->GraphicsFamily); - - BeginCommands(); - - CreateSceneVertexBuffer(); - CreateSceneLightBuffer(); - CreateVertexAndIndexBuffers(); - CreateUniformBuffer(); - if (useRayQuery) - { - CreateBottomLevelAccelerationStructure(); - CreateTopLevelAccelerationStructure(); - } - CreateShaders(); - CreateRaytracePipeline(); - CreateResolvePipeline(); - - FinishCommands(); -} - -void GPURaytracer::CreateSceneVertexBuffer() -{ - size_t size = sizeof(SceneVertex) * SceneVertexBufferSize; - - sceneVertexBuffer = BufferBuilder() - .Usage( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) - .MemoryType( - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) - .Size(size) - .DebugName("SceneVertexBuffer") - .Create(device.get()); - - sceneVertices = (SceneVertex*)sceneVertexBuffer->Map(0, size); - sceneVertexPos = 0; -} - -void GPURaytracer::CreateSceneLightBuffer() -{ - size_t size = sizeof(LightInfo) * SceneLightBufferSize; - - sceneLightBuffer = BufferBuilder() - .Usage( - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - VMA_MEMORY_USAGE_UNKNOWN, VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT) - .MemoryType( - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) - .Size(size) - .DebugName("SceneLightBuffer") - .Create(device.get()); - - sceneLights = (LightInfo*)sceneLightBuffer->Map(0, size); - sceneLightPos = 0; -} - -void GPURaytracer::BeginCommands() -{ - cmdbuffer = cmdpool->createBuffer(); - cmdbuffer->begin(); -} - -void GPURaytracer::FinishCommands() -{ - cmdbuffer->end(); - - QueueSubmit() - .AddCommandBuffer(cmdbuffer.get()) - .Execute(device.get(), device->GraphicsQueue, submitFence.get()); - - VkResult result = vkWaitForFences(device->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits::max()); - if (result != VK_SUCCESS) - throw std::runtime_error("vkWaitForFences failed"); - result = vkResetFences(device->device, 1, &submitFence->fence); - if (result != VK_SUCCESS) - throw std::runtime_error("vkResetFences failed"); - cmdbuffer.reset(); -} - -void GPURaytracer::CreateVertexAndIndexBuffers() -{ - std::vector surfaces = CreateSurfaceInfo(); - std::vector portals = CreatePortalInfo(); - std::vector nodes = CreateCollisionNodes(); - - // std430 alignment rules forces us to convert the vec3 to a vec4 - std::vector vertices; - vertices.reserve(mesh->MeshVertices.Size()); - for (const vec3& v : mesh->MeshVertices) - vertices.push_back({ v, 1.0f }); - - CollisionNodeBufferHeader nodesHeader; - nodesHeader.root = mesh->Collision->get_root(); - - vertexBuffer = BufferBuilder() - .Usage( - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT | - (useRayQuery ? - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | - VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) - .Size(vertices.size() * sizeof(vec4)) - .DebugName("vertexBuffer") - .Create(device.get()); - - indexBuffer = BufferBuilder() - .Usage( - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT | - (useRayQuery ? - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | - VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) - .Size((size_t)mesh->MeshElements.Size() * sizeof(uint32_t)) - .DebugName("indexBuffer") - .Create(device.get()); - - surfaceIndexBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size((size_t)mesh->MeshSurfaces.Size() * sizeof(uint32_t)) - .DebugName("surfaceIndexBuffer") - .Create(device.get()); - - surfaceBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size(surfaces.size() * sizeof(SurfaceInfo)) - .DebugName("surfaceBuffer") - .Create(device.get()); - - portalBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size(portals.size() * sizeof(PortalInfo)) - .DebugName("portalBuffer") - .Create(device.get()); - - nodesBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size(sizeof(CollisionNodeBufferHeader) + nodes.size() * sizeof(CollisionNode)) - .DebugName("nodesBuffer") - .Create(device.get()); - - transferBuffer = BufferTransfer() - .AddBuffer(vertexBuffer.get(), vertices.data(), vertices.size() * sizeof(vec4)) - .AddBuffer(indexBuffer.get(), mesh->MeshElements.Data(), (size_t)mesh->MeshElements.Size() * sizeof(uint32_t)) - .AddBuffer(surfaceIndexBuffer.get(), mesh->MeshSurfaces.Data(), (size_t)mesh->MeshSurfaces.Size() * sizeof(uint32_t)) - .AddBuffer(surfaceBuffer.get(), surfaces.data(), surfaces.size() * sizeof(SurfaceInfo)) - .AddBuffer(portalBuffer.get(), portals.data(), portals.size() * sizeof(PortalInfo)) - .AddBuffer(nodesBuffer.get(), &nodesHeader, sizeof(CollisionNodeBufferHeader), nodes.data(), nodes.size() * sizeof(CollisionNode)) - .Execute(device.get(), cmdbuffer.get()); - - PipelineBarrier() - .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, useRayQuery ? VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); -} - -void GPURaytracer::CreateBottomLevelAccelerationStructure() -{ - VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; - VkAccelerationStructureGeometryKHR accelStructBLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; - VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructBLDesc }; - VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; - VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; - - accelStructBLDesc.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; - accelStructBLDesc.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; - accelStructBLDesc.geometry.triangles = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR }; - accelStructBLDesc.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT; - accelStructBLDesc.geometry.triangles.vertexData.deviceAddress = vertexBuffer->GetDeviceAddress(); - accelStructBLDesc.geometry.triangles.vertexStride = sizeof(vec4); - accelStructBLDesc.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; - accelStructBLDesc.geometry.triangles.indexData.deviceAddress = indexBuffer->GetDeviceAddress(); - accelStructBLDesc.geometry.triangles.maxVertex = mesh->MeshVertices.Size() - 1; - - buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; - buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; - buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; - buildInfo.geometryCount = 1; - buildInfo.pGeometries = &accelStructBLDesc; - - uint32_t maxPrimitiveCount = mesh->MeshElements.Size() / 3; - - VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; - vkGetAccelerationStructureBuildSizesKHR(device->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo); - - blAccelStructBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) - .Size(sizeInfo.accelerationStructureSize) - .DebugName("blAccelStructBuffer") - .Create(device.get()); - - blAccelStruct = AccelerationStructureBuilder() - .Type(VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) - .Buffer(blAccelStructBuffer.get(), sizeInfo.accelerationStructureSize) - .Create(device.get()); - - blScratchBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) - .Size(sizeInfo.buildScratchSize) - .MinAlignment(device->PhysicalDevice.Properties.AccelerationStructure.minAccelerationStructureScratchOffsetAlignment) - .DebugName("blScratchBuffer") - .Create(device.get()); - - buildInfo.dstAccelerationStructure = blAccelStruct->accelstruct; - buildInfo.scratchData.deviceAddress = blScratchBuffer->GetDeviceAddress(); - rangeInfo.primitiveCount = maxPrimitiveCount; - - cmdbuffer->buildAccelerationStructures(1, &buildInfo, rangeInfos); - - // Finish building before using it as input to a toplevel accel structure - PipelineBarrier() - .AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); -} - -void GPURaytracer::CreateTopLevelAccelerationStructure() -{ - VkAccelerationStructureInstanceKHR instance = {}; - instance.transform.matrix[0][0] = 1.0f; - instance.transform.matrix[1][1] = 1.0f; - instance.transform.matrix[2][2] = 1.0f; - instance.mask = 0xff; - instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; - instance.accelerationStructureReference = blAccelStruct->GetDeviceAddress(); - - tlTransferBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY) - .Size(sizeof(VkAccelerationStructureInstanceKHR)) - .DebugName("tlTransferBuffer") - .Create(device.get()); - - auto data = (uint8_t*)tlTransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR)); - memcpy(data, &instance, sizeof(VkAccelerationStructureInstanceKHR)); - tlTransferBuffer->Unmap(); - - tlInstanceBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size(sizeof(VkAccelerationStructureInstanceKHR)) - .DebugName("tlInstanceBuffer") - .Create(device.get()); - - cmdbuffer->copyBuffer(tlTransferBuffer.get(), tlInstanceBuffer.get()); - - PipelineBarrier() - .AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); - - VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR }; - VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR }; - VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {}; - VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo }; - - accelStructTLDesc.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; - accelStructTLDesc.geometry.instances = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR }; - accelStructTLDesc.geometry.instances.data.deviceAddress = tlInstanceBuffer->GetDeviceAddress(); - - buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; - buildInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; - buildInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; - buildInfo.geometryCount = 1; - buildInfo.pGeometries = &accelStructTLDesc; - - uint32_t maxInstanceCount = 1; - - VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; - vkGetAccelerationStructureBuildSizesKHR(device->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxInstanceCount, &sizeInfo); - - tlAccelStructBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) - .Size(sizeInfo.accelerationStructureSize) - .DebugName("tlAccelStructBuffer") - .Create(device.get()); - - tlAccelStruct = AccelerationStructureBuilder() - .Type(VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) - .Buffer(tlAccelStructBuffer.get(), sizeInfo.accelerationStructureSize) - .DebugName("tlAccelStruct") - .Create(device.get()); - - tlScratchBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) - .Size(sizeInfo.buildScratchSize) - .MinAlignment(device->PhysicalDevice.Properties.AccelerationStructure.minAccelerationStructureScratchOffsetAlignment) - .DebugName("tlScratchBuffer") - .Create(device.get()); - - buildInfo.dstAccelerationStructure = tlAccelStruct->accelstruct; - buildInfo.scratchData.deviceAddress = tlScratchBuffer->GetDeviceAddress(); - rangeInfo.primitiveCount = maxInstanceCount; - - cmdbuffer->buildAccelerationStructures(1, &buildInfo, rangeInfos); - - PipelineBarrier() - .AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR) - .Execute(cmdbuffer.get(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); -} - -void GPURaytracer::CreateShaders() -{ - std::string prefix = "#version 460\r\n#line 1\r\n"; - std::string traceprefix = "#version 460\r\n"; - if (useRayQuery) - { - traceprefix += "#extension GL_EXT_ray_query : require\r\n"; - traceprefix += "#define USE_RAYQUERY\r\n"; - } - traceprefix += "#line 1\r\n"; - - vertShader = ShaderBuilder() - .Type(ShaderType::Vertex) - .AddSource("vert.glsl", prefix + glsl_vert) - .DebugName("vertShader") - .Create("vertShader", device.get()); - - fragShader = ShaderBuilder() - .Type(ShaderType::Fragment) - .AddSource("frag.glsl", traceprefix + glsl_frag) - .DebugName("fragShader") - .Create("fragShader", device.get()); - - fragResolveShader = ShaderBuilder() - .Type(ShaderType::Fragment) - .AddSource("frag_resolve.glsl", prefix + glsl_frag_resolve) - .DebugName("fragResolveShader") - .Create("fragResolveShader", device.get()); -} - -void GPURaytracer::CreateRaytracePipeline() -{ - raytrace.descriptorSetLayout0 = DescriptorSetLayoutBuilder() - .AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .DebugName("raytrace.descriptorSetLayout0") - .Create(device.get()); - - if (useRayQuery) - { - raytrace.descriptorSetLayout1 = DescriptorSetLayoutBuilder() - .AddBinding(0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .DebugName("raytrace.descriptorSetLayout1") - .Create(device.get()); - } - else - { - raytrace.descriptorSetLayout1 = DescriptorSetLayoutBuilder() - .AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .DebugName("raytrace.descriptorSetLayout1") - .Create(device.get()); - } - - raytrace.pipelineLayout = PipelineLayoutBuilder() - .AddSetLayout(raytrace.descriptorSetLayout0.get()) - .AddSetLayout(raytrace.descriptorSetLayout1.get()) - .AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants)) - .DebugName("raytrace.pipelineLayout") - .Create(device.get()); - - raytrace.renderPassBegin = RenderPassBuilder() - .AddAttachment( - VK_FORMAT_R16G16B16A16_SFLOAT, - VK_SAMPLE_COUNT_4_BIT, - VK_ATTACHMENT_LOAD_OP_CLEAR, - VK_ATTACHMENT_STORE_OP_STORE, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) - .AddSubpass() - .AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) - .AddExternalSubpassDependency( - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) - .DebugName("raytrace.renderPassBegin") - .Create(device.get()); - - raytrace.renderPassContinue = RenderPassBuilder() - .AddAttachment( - VK_FORMAT_R16G16B16A16_SFLOAT, - VK_SAMPLE_COUNT_4_BIT, - VK_ATTACHMENT_LOAD_OP_LOAD, - VK_ATTACHMENT_STORE_OP_STORE, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) - .AddSubpass() - .AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) - .AddExternalSubpassDependency( - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) - .DebugName("raytrace.renderPassContinue") - .Create(device.get()); - - raytrace.pipeline = GraphicsPipelineBuilder() - .Layout(raytrace.pipelineLayout.get()) - .RenderPass(raytrace.renderPassBegin.get()) - .AddVertexShader(vertShader.get()) - .AddFragmentShader(fragShader.get()) - .AddVertexBufferBinding(0, sizeof(SceneVertex)) - .AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(SceneVertex, Position)) - .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) - .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) - .RasterizationSamples(VK_SAMPLE_COUNT_4_BIT) - .Viewport(0.0f, 0.0f, 0.0f, 0.0f) - .Scissor(0, 0, 4096, 4096) - .DebugName("raytrace.pipeline") - .Create(device.get()); - - raytrace.descriptorPool0 = DescriptorPoolBuilder() - .AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1) - .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4) - .MaxSets(1) - .DebugName("raytrace.descriptorPool0") - .Create(device.get()); - - if (useRayQuery) - { - raytrace.descriptorPool1 = DescriptorPoolBuilder() - .AddPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1) - .MaxSets(1) - .DebugName("raytrace.descriptorPool1") - .Create(device.get()); - } - else - { - raytrace.descriptorPool1 = DescriptorPoolBuilder() - .AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3) - .MaxSets(1) - .DebugName("raytrace.descriptorPool1") - .Create(device.get()); - } - - raytrace.descriptorSet0 = raytrace.descriptorPool0->allocate(raytrace.descriptorSetLayout0.get()); - raytrace.descriptorSet0->SetDebugName("raytrace.descriptorSet1"); - - raytrace.descriptorSet1 = raytrace.descriptorPool1->allocate(raytrace.descriptorSetLayout1.get()); - raytrace.descriptorSet1->SetDebugName("raytrace.descriptorSet1"); - - if (useRayQuery) - { - WriteDescriptors() - .AddAccelerationStructure(raytrace.descriptorSet1.get(), 0, tlAccelStruct.get()) - .Execute(device.get()); - } - else - { - WriteDescriptors() - .AddBuffer(raytrace.descriptorSet1.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nodesBuffer.get()) - .AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vertexBuffer.get()) - .AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, indexBuffer.get()) - .Execute(device.get()); - } - - WriteDescriptors() - .AddBuffer(raytrace.descriptorSet0.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniformBuffer.get(), 0, sizeof(Uniforms)) - .AddBuffer(raytrace.descriptorSet0.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceIndexBuffer.get()) - .AddBuffer(raytrace.descriptorSet0.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, surfaceBuffer.get()) - .AddBuffer(raytrace.descriptorSet0.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, sceneLightBuffer.get()) - .AddBuffer(raytrace.descriptorSet0.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, portalBuffer.get()) - .Execute(device.get()); -} - -void GPURaytracer::CreateResolvePipeline() -{ - resolve.descriptorSetLayout = DescriptorSetLayoutBuilder() - .AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT) - .DebugName("resolve.descriptorSetLayout") - .Create(device.get()); - - resolve.pipelineLayout = PipelineLayoutBuilder() - .AddSetLayout(resolve.descriptorSetLayout.get()) - .AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstants)) - .DebugName("resolve.pipelineLayout") - .Create(device.get()); - - resolve.renderPass = RenderPassBuilder() - .AddAttachment( - VK_FORMAT_R16G16B16A16_SFLOAT, - VK_SAMPLE_COUNT_1_BIT, - VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_STORE, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) - .AddSubpass() - .AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) - .AddExternalSubpassDependency( - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) - .DebugName("resolve.renderpass") - .Create(device.get()); - - resolve.pipeline = GraphicsPipelineBuilder() - .Layout(resolve.pipelineLayout.get()) - .RenderPass(resolve.renderPass.get()) - .AddVertexShader(vertShader.get()) - .AddFragmentShader(fragResolveShader.get()) - .AddVertexBufferBinding(0, sizeof(SceneVertex)) - .AddVertexAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(SceneVertex, Position)) - .Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) - .AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT) - .Viewport(0.0f, 0.0f, 0.0f, 0.0f) - .Scissor(0, 0, 4096, 4096) - .DebugName("resolve.pipeline") - .Create(device.get()); - - resolve.descriptorPool = DescriptorPoolBuilder() - .AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 256) - .MaxSets(256) - .DebugName("resolve.descriptorPool") - .Create(device.get()); - - resolve.sampler = SamplerBuilder() - .DebugName("resolve.Sampler") - .Create(device.get()); -} - -LightmapImage GPURaytracer::CreateImage(int width, int height) -{ - LightmapImage img; - - img.raytrace.Image = ImageBuilder() - .Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT) - .Format(VK_FORMAT_R16G16B16A16_SFLOAT) - .Size(width, height) - .Samples(VK_SAMPLE_COUNT_4_BIT) - .DebugName("LightmapImage.raytrace.Image") - .Create(device.get()); - - img.raytrace.View = ImageViewBuilder() - .Image(img.raytrace.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) - .DebugName("LightmapImage.raytrace.View") - .Create(device.get()); - - img.raytrace.Framebuffer = FramebufferBuilder() - .RenderPass(raytrace.renderPassBegin.get()) - .Size(width, height) - .AddAttachment(img.raytrace.View.get()) - .DebugName("LightmapImage.raytrace.Framebuffer") - .Create(device.get()); - - img.resolve.Image = ImageBuilder() - .Usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) - .Format(VK_FORMAT_R16G16B16A16_SFLOAT) - .Size(width, height) - .DebugName("LightmapImage.resolve.Image") - .Create(device.get()); - - img.resolve.View = ImageViewBuilder() - .Image(img.resolve.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT) - .DebugName("LightmapImage.resolve.View") - .Create(device.get()); - - img.resolve.Framebuffer = FramebufferBuilder() - .RenderPass(resolve.renderPass.get()) - .Size(width, height) - .AddAttachment(img.resolve.View.get()) - .DebugName("LightmapImage.resolve.Framebuffer") - .Create(device.get()); - - img.Transfer = BufferBuilder() - .Size(width * height * sizeof(vec4)) - .Usage(VK_IMAGE_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY) - .DebugName("LightmapImage.Transfer") - .Create(device.get()); - - return img; -} - -void GPURaytracer::CreateUniformBuffer() -{ - VkDeviceSize align = device->PhysicalDevice.Properties.Properties.limits.minUniformBufferOffsetAlignment; - uniformStructStride = (sizeof(Uniforms) + align - 1) / align * align; - - uniformBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT) - .Size(uniformStructs * uniformStructStride) - .DebugName("uniformBuffer") - .Create(device.get()); - - uniformTransferBuffer = BufferBuilder() - .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU) - .Size(uniformStructs * uniformStructStride) - .DebugName("uniformTransferBuffer") - .Create(device.get()); -} - -std::vector GPURaytracer::CreateSurfaceInfo() -{ - std::vector surfaces; - surfaces.reserve(mesh->surfaces.size()); - for (const auto& surface : mesh->surfaces) - { - SurfaceInfo info; - info.Sky = surface->bSky ? 1.0f : 0.0f; - info.Normal = surface->plane.Normal(); - info.SamplingDistance = float(surface->sampleDimension); - - if (surface->portalIndex >= 0) - { - info.PortalIndex = surface->portalIndex + 1; // Index 0 is already occupied. See GPURaytracer::CreatePortalInfo() - } - else - { - info.PortalIndex = 0; - } - - surfaces.push_back(info); - } - if (surfaces.empty()) // vulkan doesn't support zero byte buffers - surfaces.push_back(SurfaceInfo()); - return surfaces; -} - -std::vector GPURaytracer::CreatePortalInfo() -{ - std::vector portals; - { - PortalInfo noPortal; - noPortal.Transformation = mat4::identity(); // index 0 will always contain identity matrix (for convenience) - portals.push_back(noPortal); - } - - for (const auto& portal : mesh->portals) - { - PortalInfo info; - info.Transformation = portal->transformation; - portals.push_back(info); - } - - return portals; -} - -std::vector GPURaytracer::CreateCollisionNodes() -{ - std::vector nodes; - nodes.reserve(mesh->Collision->get_nodes().size()); - for (const auto& node : mesh->Collision->get_nodes()) - { - CollisionNode info; - info.center = node.aabb.Center; - info.extents = node.aabb.Extents; - info.left = node.left; - info.right = node.right; - info.element_index = node.element_index; - nodes.push_back(info); - } - if (nodes.empty()) // vulkan doesn't support zero byte buffers - nodes.push_back(CollisionNode()); - return nodes; -} - -void GPURaytracer::PrintVulkanInfo() -{ - const auto& props = device->PhysicalDevice.Properties.Properties; - - std::string deviceType; - switch (props.deviceType) - { - case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break; - case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break; - case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break; - case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break; - case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break; - default: deviceType = std::to_string(props.deviceType); break; - } - - std::string apiVersion = std::to_string(VK_VERSION_MAJOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.apiVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.apiVersion)); - std::string driverVersion = std::to_string(VK_VERSION_MAJOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_MINOR(props.driverVersion)) + "." + std::to_string(VK_VERSION_PATCH(props.driverVersion)); - - printf(" Vulkan device: %s\n", props.deviceName); - printf(" Vulkan device type: %s\n", deviceType.c_str()); - printf(" Vulkan version: %s (api) %s (driver)\n", apiVersion.c_str(), driverVersion.c_str()); -} - -void GPURaytracer::LoadRenderDoc() -{ -#ifdef _WIN32 - if (auto mod = GetModuleHandleA("renderdoc.dll")) - { - pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI"); - int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&rdoc_api); - assert(ret == 1); - - if (ret != 1) - { - printf(" RENDERDOC_GetAPI returned %d\n", ret); - } - } -#else - if (void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD)) - { - pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI"); - int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&rdoc_api); - assert(ret == 1); - - if (ret != 1) - { - printf(" RENDERDOC_GetAPI returned %d\n", ret); - } - } -#endif - - if (rdoc_api) - { - printf(" RenderDoc enabled\n"); - } -} - -void VulkanPrintLog(const char* typestr, const std::string& msg) -{ - printf(" [%s] %s\n", typestr, msg.c_str()); - printf(" %s\n", CaptureStackTraceText(2).c_str()); -} - -void VulkanError(const char* text) -{ - throw std::runtime_error(text); -} diff --git a/src/lightmap/gpuraytracer.h b/src/lightmap/gpuraytracer.h deleted file mode 100644 index 635ebe6..0000000 --- a/src/lightmap/gpuraytracer.h +++ /dev/null @@ -1,228 +0,0 @@ - -#pragma once - -#include -#include - -class LevelMesh; - -struct Uniforms -{ - vec3 SunDir; - float Padding1; - vec3 SunColor; - float SunIntensity; -}; - -struct PushConstants -{ - uint32_t LightStart; - uint32_t LightEnd; - int32_t SurfaceIndex; - int32_t PushPadding1; - vec3 LightmapOrigin; - float PushPadding2; - vec3 LightmapStepX; - float PushPadding3; - vec3 LightmapStepY; - float PushPadding4; -}; - -struct SurfaceInfo -{ - vec3 Normal; - float Sky; - float SamplingDistance; - uint32_t PortalIndex; - float Padding1, Padding2; -}; - -static_assert(sizeof(SurfaceInfo) == sizeof(float) * 8); - -struct PortalInfo -{ - mat4 Transformation; -}; - -static_assert(sizeof(PortalInfo) == sizeof(float) * 16); - -struct LightInfo -{ - vec3 Origin; - float Padding0; - vec3 RelativeOrigin; - float Padding1; - float Radius; - float Intensity; - float InnerAngleCos; - float OuterAngleCos; - vec3 SpotDir; - float Padding2; - vec3 Color; - float Padding3; -}; - -static_assert(sizeof(LightInfo) == sizeof(float) * 20); - -struct CollisionNodeBufferHeader -{ - int root; - int padding1; - int padding2; - int padding3; -}; - -struct CollisionNode -{ - vec3 center; - float padding1; - vec3 extents; - float padding2; - int left; - int right; - int element_index; - int padding3; -}; - -struct LightmapImage -{ - struct - { - std::unique_ptr Image; - std::unique_ptr View; - std::unique_ptr Framebuffer; - } raytrace; - - struct - { - std::unique_ptr Image; - std::unique_ptr View; - std::unique_ptr Framebuffer; - } resolve; - - std::unique_ptr Transfer; -}; - -struct SceneVertex -{ - vec2 Position; -}; - -class GPURaytracer -{ -public: - GPURaytracer(); - ~GPURaytracer(); - - void Raytrace(LevelMesh* level); - -private: - void CreateVulkanObjects(); - void CreateVertexAndIndexBuffers(); - void CreateBottomLevelAccelerationStructure(); - void CreateTopLevelAccelerationStructure(); - void CreateShaders(); - void CreateRaytracePipeline(); - void CreateResolvePipeline(); - void CreateUniformBuffer(); - void CreateSceneVertexBuffer(); - void CreateSceneLightBuffer(); - - void UploadUniforms(); - void CreateAtlasImages(); - void RenderAtlasImage(size_t pageIndex); - void ResolveAtlasImage(size_t pageIndex); - void DownloadAtlasImage(size_t pageIndex); - - LightmapImage CreateImage(int width, int height); - - void BeginCommands(); - void FinishCommands(); - - void LoadRenderDoc(); - void PrintVulkanInfo(); - - std::vector CreateSurfaceInfo(); - std::vector CreatePortalInfo(); - std::vector CreateCollisionNodes(); - - static vec2 ToUV(const vec3& vert, const Surface* targetSurface); - - LevelMesh* mesh = nullptr; - - uint8_t* mappedUniforms = nullptr; - int uniformsIndex = 0; - int uniformStructs = 256; - VkDeviceSize uniformStructStride = sizeof(Uniforms); - - std::shared_ptr device; - - bool useRayQuery = true; - - static const int SceneVertexBufferSize = 1 * 1024 * 1024; - std::unique_ptr sceneVertexBuffer; - SceneVertex* sceneVertices = nullptr; - int sceneVertexPos = 0; - - static const int SceneLightBufferSize = 2 * 1024 * 1024; - std::unique_ptr sceneLightBuffer; - LightInfo* sceneLights = nullptr; - int sceneLightPos = 0; - - std::unique_ptr vertexBuffer; - std::unique_ptr indexBuffer; - std::unique_ptr transferBuffer; - std::unique_ptr surfaceIndexBuffer; - std::unique_ptr surfaceBuffer; - std::unique_ptr portalBuffer; - std::unique_ptr nodesBuffer; - - std::unique_ptr blScratchBuffer; - std::unique_ptr blAccelStructBuffer; - std::unique_ptr blAccelStruct; - - std::unique_ptr tlTransferBuffer; - std::unique_ptr tlScratchBuffer; - std::unique_ptr tlInstanceBuffer; - std::unique_ptr tlAccelStructBuffer; - std::unique_ptr tlAccelStruct; - - std::unique_ptr vertShader; - std::unique_ptr fragShader; - std::unique_ptr fragResolveShader; - - struct - { - std::unique_ptr descriptorSetLayout0; - std::unique_ptr descriptorSetLayout1; - std::unique_ptr pipelineLayout; - std::unique_ptr pipeline; - std::unique_ptr renderPassBegin; - std::unique_ptr renderPassContinue; - std::unique_ptr descriptorPool0; - std::unique_ptr descriptorPool1; - std::unique_ptr descriptorSet0; - std::unique_ptr descriptorSet1; - } raytrace; - - struct - { - std::unique_ptr descriptorSetLayout; - std::unique_ptr pipelineLayout; - std::unique_ptr pipeline; - std::unique_ptr renderPass; - std::unique_ptr descriptorPool; - std::vector> descriptorSets; - std::unique_ptr sampler; - } resolve; - - std::unique_ptr uniformBuffer; - std::unique_ptr uniformTransferBuffer; - - std::unique_ptr submitFence; - std::unique_ptr cmdpool; - std::unique_ptr cmdbuffer; - - std::vector atlasImages; - static const int atlasImageSize = 2048; -}; diff --git a/src/lightmap/levelmesh.cpp b/src/lightmap/levelmesh.cpp deleted file mode 100644 index e6a74b9..0000000 --- a/src/lightmap/levelmesh.cpp +++ /dev/null @@ -1,1546 +0,0 @@ -//----------------------------------------------------------------------------- -// Note: this is a modified version of dlight. It is not the original software. -//----------------------------------------------------------------------------- -// -// Copyright (c) 2013-2014 Samuel Villarreal -// svkaiser@gmail.com -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// - -#include "math/mathlib.h" -#include "framework/templates.h" -#include "framework/halffloat.h" -#include "framework/binfile.h" -#include "level/level.h" -#include "levelmesh.h" -#include "pngwriter.h" -#include - -#ifdef _MSC_VER -#pragma warning(disable: 4267) // warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data -#pragma warning(disable: 4244) // warning C4244: '=': conversion from '__int64' to 'int', possible loss of data -#endif - -LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize) -{ - map = &doomMap; - defaultSamples = sampleDistance; - textureWidth = textureSize; - textureHeight = textureSize; - - for (unsigned int i = 0; i < doomMap.Sides.Size(); i++) - { - CreateSideSurfaces(doomMap, &doomMap.Sides[i]); - printf(" Sides: %i / %i\r", i + 1, doomMap.Sides.Size()); - } - - printf("\n Side surfaces: %i\n", (int)surfaces.size()); - - CreateSubsectorSurfaces(doomMap); - - printf(" Surfaces total: %i\n", (int)surfaces.size()); - - // Update sector group of the surfacesaa - for (auto& surface : surfaces) - { - surface->sectorGroup = surface->type == ST_CEILING || surface->type == ST_FLOOR ? - doomMap.GetSectorFromSubSector(&doomMap.GLSubsectors[surface->typeIndex])->group : (doomMap.Sides[surface->typeIndex].GetSectorGroup()); - } - - printf(" Building level mesh...\n"); - - for (size_t i = 0; i < surfaces.size(); i++) - { - const auto &s = surfaces[i]; - int numVerts = s->verts.size(); - unsigned int pos = MeshVertices.Size(); - - for (int j = 0; j < numVerts; j++) - { - MeshVertices.Push(s->verts[j]); - MeshUVIndex.Push(j); - } - - if (s->type == ST_FLOOR || s->type == ST_CEILING) - { - for (int j = 2; j < numVerts; j++) - { - if (!IsDegenerate(s->verts[0], s->verts[j - 1], s->verts[j])) - { - MeshElements.Push(pos); - MeshElements.Push(pos + j - 1); - MeshElements.Push(pos + j); - MeshSurfaces.Push(i); - } - } - } - else if (s->type == ST_MIDDLESIDE || s->type == ST_UPPERSIDE || s->type == ST_LOWERSIDE) - { - if (!IsDegenerate(s->verts[0], s->verts[1], s->verts[2])) - { - MeshElements.Push(pos + 0); - MeshElements.Push(pos + 1); - MeshElements.Push(pos + 2); - MeshSurfaces.Push(i); - } - if (!IsDegenerate(s->verts[1], s->verts[2], s->verts[3])) - { - MeshElements.Push(pos + 3); - MeshElements.Push(pos + 2); - MeshElements.Push(pos + 1); - MeshSurfaces.Push(i); - } - } - } - - for (size_t i = 0; i < surfaces.size(); i++) - { - BuildSurfaceParams(surfaces[i].get()); - } - - printf(" Finding smoothing groups...\n"); - BuildSmoothingGroups(doomMap); - printf(" Building collision data...\n"); - - Collision = std::make_unique(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size()); - - BuildLightLists(doomMap); - /* - std::map lightStats; - for (auto& surface : surfaces) - lightStats[surface->LightList.size()]++; - for (auto& it : lightStats) - printf(" %d lights: %d surfaces\n", it.first, it.second); - printf("\n"); - */ -} - -void LevelMesh::BuildSmoothingGroups(FLevel& doomMap) -{ - size_t lastPercentage = 0; - for (size_t i = 0; i < surfaces.size(); i++) - { - size_t percentage = i * 100 / surfaces.size(); - if (lastPercentage != percentage) - printf(" Surfaces: %i%%\r", (int)percentage); - - // Is this surface in the same plane as an existing smoothing group? - int smoothingGroupIndex = -1; - - auto surface = surfaces[i].get(); - - for (size_t j = 0; j < smoothingGroups.size(); j++) - { - if (surface->sectorGroup == smoothingGroups[j].sectorGroup) - { - float direction = std::abs(dot(smoothingGroups[j].plane.Normal(), surface->plane.Normal())); - if (direction >= 0.9999f && direction <= 1.001f) - { - float dist = std::abs(smoothingGroups[j].plane.Distance(surface->plane.Normal() * surface->plane.d)); - if (dist <= 0.01f) - { - smoothingGroupIndex = (int)j; - break; - } - } - } - } - - // Surface is in a new plane. Create a smoothing group for it - if (smoothingGroupIndex == -1) - { - smoothingGroupIndex = smoothingGroups.size(); - - SmoothingGroup group; - group.plane = surface->plane; - group.sectorGroup = surface->sectorGroup; - smoothingGroups.push_back(group); - } - - smoothingGroups[smoothingGroupIndex].surfaces.push_back(surface); - surface->smoothingGroupIndex = smoothingGroupIndex; - } - - printf(" Created %d smoothing groups for %d surfaces\n", (int)smoothingGroups.size(), (int)surfaces.size()); -} - -void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light) -{ - if (lightPropagationRecursiveDepth > 32) - { - return; - } - - SphereShape sphere; - sphere.center = light->LightRelativeOrigin(); - sphere.radius = light->LightRadius(); - lightPropagationRecursiveDepth++; - std::set portalsToErase; - for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere)) - { - Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get(); - - // skip any surface which isn't physically connected to the sector group in which the light resides - if (light->sectorGroup == surface->sectorGroup) - { - if (surface->portalIndex >= 0) - { - auto portal = portals[surface->portalIndex].get(); - - if (touchedPortals.insert(*portal).second) - { - auto fakeLight = std::make_unique(*light); - - fakeLight->relativePosition.emplace(portal->TransformPosition(light->LightRelativeOrigin())); - fakeLight->sectorGroup = portal->targetSectorGroup; - - PropagateLight(doomMap, fakeLight.get()); - portalsToErase.insert(*portal); - portalLights.push_back(std::move(fakeLight)); - } - } - - // Add light to the list if it isn't already there - bool found = false; - for (ThingLight* light2 : surface->LightList) - { - if (light2 == light) - { - found = true; - break; - } - } - if (!found) - surface->LightList.push_back(light); - } - } - - for (auto& portal : portalsToErase) - { - touchedPortals.erase(portal); - } - - lightPropagationRecursiveDepth--; -} - -void LevelMesh::BuildLightLists(FLevel& doomMap) -{ - for (unsigned i = 0; i < map->ThingLights.Size(); ++i) - { - printf(" Building light lists: %u / %u\r", i, map->ThingLights.Size()); - PropagateLight(doomMap, &map->ThingLights[i]); - } - - printf(" Building light lists: %u / %u\n", map->ThingLights.Size(), map->ThingLights.Size()); -} - -// Determines a lightmap block in which to map to the lightmap texture. -// Width and height of the block is calcuated and steps are computed to determine where each texel will be positioned on the surface -void LevelMesh::BuildSurfaceParams(Surface* surface) -{ - Plane* plane; - BBox bounds; - vec3 roundedSize; - int i; - Plane::PlaneAxis axis; - vec3 tCoords[2]; - vec3 tOrigin; - int width; - int height; - float d; - - plane = &surface->plane; - bounds = GetBoundsFromSurface(surface); - surface->bounds = bounds; - - if (surface->sampleDimension < 0) surface->sampleDimension = 1; - surface->sampleDimension = Math::RoundPowerOfTwo(surface->sampleDimension); - - // round off dimensions - for (i = 0; i < 3; i++) - { - bounds.min[i] = surface->sampleDimension * (Math::Floor(bounds.min[i] / surface->sampleDimension) - 1); - bounds.max[i] = surface->sampleDimension * (Math::Ceil(bounds.max[i] / surface->sampleDimension) + 1); - - roundedSize[i] = (bounds.max[i] - bounds.min[i]) / surface->sampleDimension; - } - - tCoords[0] = vec3(0.0f); - tCoords[1] = vec3(0.0f); - - axis = plane->BestAxis(); - - switch (axis) - { - case Plane::AXIS_YZ: - width = (int)roundedSize.y; - height = (int)roundedSize.z; - tCoords[0].y = 1.0f / surface->sampleDimension; - tCoords[1].z = 1.0f / surface->sampleDimension; - break; - - case Plane::AXIS_XZ: - width = (int)roundedSize.x; - height = (int)roundedSize.z; - tCoords[0].x = 1.0f / surface->sampleDimension; - tCoords[1].z = 1.0f / surface->sampleDimension; - break; - - case Plane::AXIS_XY: - width = (int)roundedSize.x; - height = (int)roundedSize.y; - tCoords[0].x = 1.0f / surface->sampleDimension; - tCoords[1].y = 1.0f / surface->sampleDimension; - break; - } - - // clamp width - if (width > textureWidth - 2) - { - tCoords[0] *= ((float)(textureWidth - 2) / (float)width); - width = (textureWidth - 2); - } - - // clamp height - if (height > textureHeight - 2) - { - tCoords[1] *= ((float)(textureHeight - 2) / (float)height); - height = (textureHeight - 2); - } - - surface->translateWorldToLocal = bounds.min; - surface->projLocalToU = tCoords[0]; - surface->projLocalToV = tCoords[1]; - - surface->lightUV.resize(surface->verts.size()); - for (i = 0; i < (int)surface->verts.size(); i++) - { - vec3 tDelta = surface->verts[i] - surface->translateWorldToLocal; - surface->lightUV[i].x = dot(tDelta, surface->projLocalToU); - surface->lightUV[i].y = dot(tDelta, surface->projLocalToV); - } - - tOrigin = bounds.min; - - // project tOrigin and tCoords so they lie on the plane - d = (plane->Distance(bounds.min)) / plane->Normal()[axis]; - tOrigin[axis] -= d; - - for (i = 0; i < 2; i++) - { - tCoords[i] = normalize(tCoords[i]); - d = dot(tCoords[i], plane->Normal()) / plane->Normal()[axis]; - tCoords[i][axis] -= d; - } - - surface->texWidth = width; - surface->texHeight = height; - surface->texPixels.resize(width * height); - surface->worldOrigin = tOrigin; - surface->worldStepX = tCoords[0] * (float)surface->sampleDimension; - surface->worldStepY = tCoords[1] * (float)surface->sampleDimension; -} - -BBox LevelMesh::GetBoundsFromSurface(const Surface* surface) -{ - vec3 low(M_INFINITY, M_INFINITY, M_INFINITY); - vec3 hi(-M_INFINITY, -M_INFINITY, -M_INFINITY); - - BBox bounds; - bounds.Clear(); - - for (int i = 0; i < (int)surface->verts.size(); i++) - { - for (int j = 0; j < 3; j++) - { - if (surface->verts[i][j] < low[j]) - { - low[j] = surface->verts[i][j]; - } - if (surface->verts[i][j] > hi[j]) - { - hi[j] = surface->verts[i][j]; - } - } - } - - bounds.min = low; - bounds.max = hi; - - return bounds; -} - -void LevelMesh::CreateTextures() -{ - BlurSurfaces(); - - std::vector sortedSurfaces; - sortedSurfaces.reserve(surfaces.size()); - - for (auto& surface : surfaces) - { - int sampleWidth = surface->texWidth; - int sampleHeight = surface->texHeight; - vec3* colorSamples = surface->texPixels.data(); - - // SVE redraws the scene for lightmaps, so for optimizations, - // tell the engine to ignore this surface if completely black - bool bShouldLookupTexture = false; - for (int i = 0; i < sampleHeight; i++) - { - for (int j = 0; j < sampleWidth; j++) - { - const auto& c = colorSamples[i * sampleWidth + j]; - if (c.x > 0.0f || c.y > 0.0f || c.z > 0.0f) - { - bShouldLookupTexture = true; - break; - } - } - } - - if (bShouldLookupTexture) - { - sortedSurfaces.push_back(surface.get()); - surface->atlasPageIndex = 0; - } - else - { - surface->atlasPageIndex = -1; - } - } - - std::sort(sortedSurfaces.begin(), sortedSurfaces.end(), [](Surface* a, Surface* b) { return a->texHeight != b->texHeight ? a->texHeight > b->texHeight : a->texWidth > b->texWidth; }); - -#if 0 // LIGHTMAP V2 avoids this - RectPacker packer(textureWidth, textureHeight, RectPacker::Spacing(0)); - - for (Surface* surf : sortedSurfaces) - { - FinishSurface(packer, surf); - } -#endif -} - -void LevelMesh::BlurSurfaces() -{ - static const float weights[9] = { 0.125f, 0.25f, 0.125f, 0.25f, 0.50f, 0.25f, 0.125f, 0.25f, 0.125f }; - std::vector tempBuffer; - - for (auto& surface : surfaces) - { - int texWidth = surface->texWidth; - int texHeight = surface->texHeight; - vec3* texPixels = surface->texPixels.data(); - - tempBuffer.resize(std::max(tempBuffer.size(), (size_t)texWidth * texHeight)); - vec3* tempPixels = tempBuffer.data(); - - // gaussian blur with a 3x3 kernel - for (int y = 0; y < texHeight; y++) - { - vec3* src = &texPixels[y * texWidth]; - vec3* dst = &tempPixels[y * texWidth]; - for (int x = 0; x < texWidth; x++) - { - vec3 color = { 0.0f }; - for (int yy = -1; yy <= 1; yy++) - { - int yyy = clamp(y + yy, 0, texHeight - 1) - y; - for (int xx = -1; xx <= 1; xx++) - { - int xxx = clamp(x + xx, 0, texWidth - 1); - color += src[yyy * texWidth + xxx] * weights[4 + xx + yy * 3]; - } - } - dst[x] = color * 0.5f; - } - } - - memcpy(texPixels, tempPixels, texWidth * texHeight * sizeof(vec3)); - } -} - -void LevelMesh::FinishSurface(RectPacker& packer, Surface* surface) -{ - int sampleWidth = surface->texWidth; - int sampleHeight = surface->texHeight; - vec3* colorSamples = surface->texPixels.data(); - - auto result = packer.insert(sampleWidth, sampleHeight); - int x = result.pos.x, y = result.pos.y; - surface->atlasPageIndex = result.pageIndex; - - while (result.pageIndex >= textures.size()) - { - textures.push_back(std::make_unique(textureWidth, textureHeight)); - } - - uint16_t* currentTexture = textures[surface->atlasPageIndex]->Pixels(); - - // calculate final texture coordinates - for (int i = 0; i < (int)surface->verts.size(); i++) - { - auto& u = surface->lightUV[i].x; - auto& v = surface->lightUV[i].y; - u = (u + x) / (float)textureWidth; - v = (v + y) / (float)textureHeight; - } - - surface->atlasX = x; - surface->atlasY = y; - - // store results to lightmap texture - for (int i = 0; i < sampleHeight; i++) - { - for (int j = 0; j < sampleWidth; j++) - { - // get texture offset - int offs = ((textureWidth * (i + surface->atlasY)) + surface->atlasX) * 3; - - // convert RGB to bytes - currentTexture[offs + j * 3 + 0] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].x, 0.0f, 65000.0f)); - currentTexture[offs + j * 3 + 1] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].y, 0.0f, 65000.0f)); - currentTexture[offs + j * 3 + 2] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].z, 0.0f, 65000.0f)); - } - } -} - -int LevelMesh::CreateLinePortal(FLevel& doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine) -{ - auto portal = std::make_unique(); - - // Calculate portal transformation - { - FloatVertex srcV1 = doomMap.GetSegVertex(srcLine.v1); - FloatVertex srcV2 = doomMap.GetSegVertex(srcLine.v2); - FloatVertex dstV1 = doomMap.GetSegVertex(dstLine.v1); - FloatVertex dstV2 = doomMap.GetSegVertex(dstLine.v2); - - int alignment = srcLine.args[3]; - - double srcAZ = 0; - double srcBZ = 0; - double dstAZ = 0; - double dstBZ = 0; - - const auto* srcFront = srcLine.frontsector; - const auto* dstFront = dstLine.frontsector; - - if (alignment == 1) // floor - { - srcAZ = srcFront->floorplane.zAt(srcV1.x, srcV1.y); - srcBZ = srcFront->floorplane.zAt(srcV2.x, srcV2.y); - dstAZ = dstFront->floorplane.zAt(dstV1.x, dstV1.y); - dstBZ = dstFront->floorplane.zAt(dstV2.x, dstV2.y); - } - else if (alignment == 2) // ceiling - { - srcAZ = srcFront->ceilingplane.zAt(srcV1.x, srcV1.y); - srcBZ = srcFront->ceilingplane.zAt(srcV2.x, srcV2.y); - dstAZ = dstFront->ceilingplane.zAt(dstV1.x, dstV1.y); - dstBZ = dstFront->ceilingplane.zAt(dstV2.x, dstV2.y); - } - - const vec3 vecSrcA = vec3(vec2(srcV1.x, srcV1.y), srcAZ); - const vec3 vecSrcB = vec3(vec2(srcV2.x, srcV2.y), srcAZ); - const vec3 vecDstA = vec3(vec2(dstV1.x, dstV1.y), dstAZ); - const vec3 vecDstB = vec3(vec2(dstV2.x, dstV2.y), dstBZ); - - // Translation - vec3 originSrc = (vecSrcB + vecSrcA) * 0.5f; - vec3 originDst = (vecDstB + vecDstA) * 0.5f; - - vec3 translation = originDst - originSrc; - - // Rotation - // TODO :( - - // printf(" Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z); - - portal->transformation = mat4::translate(translation); - portal->sourceSectorGroup = srcLine.GetSectorGroup(); - portal->targetSectorGroup = dstLine.GetSectorGroup(); - } - - // Deduplicate portals - auto it = portalCache.find(*portal); - - if (it == portalCache.end()) - { - int id = int(portals.size()); - portalCache.emplace(*portal, id); - portals.push_back(std::move(portal)); - return id; - } - return it->second; -} - -int LevelMesh::CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane) -{ - for (const auto line : sector->portals) - { - if (line->special == Sector_SetPortal && line->args[0] && line->args[2] == plane && !line->args[3] && sector->HasTag(line->args[0])) - { - const IntLineDef* dstLine = nullptr; - - // Find the other portal line - for (const auto &targetLine : doomMap.Lines) - { - if (targetLine.special == Sector_SetPortal && targetLine.args[2] == plane && targetLine.args[3] && line->args[0] == targetLine.args[0]) - { - dstLine = &targetLine; - break; - } - } - - if (dstLine) - { - return CreatePlanePortal(doomMap, *line, *dstLine); - } - } - } - - return -1; -} - -int LevelMesh::CreatePlanePortal(FLevel& doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine) -{ - auto portal = std::make_unique(); - - // Calculate portal transformation - { - FloatVertex srcV1 = doomMap.GetSegVertex(srcLine.v1); - FloatVertex srcV2 = doomMap.GetSegVertex(srcLine.v2); - FloatVertex dstV1 = doomMap.GetSegVertex(dstLine.v1); - FloatVertex dstV2 = doomMap.GetSegVertex(dstLine.v2); - - int alignment = srcLine.args[2]; - - double srcAZ = 0; - double srcBZ = 0; - double dstAZ = 0; - double dstBZ = 0; - - const auto* srcFront = srcLine.frontsector; - const auto* dstFront = dstLine.frontsector; - - if (alignment == 0) // floor - { - srcAZ = srcFront->floorplane.zAt(srcV1.x, srcV1.y); - srcBZ = srcFront->floorplane.zAt(srcV2.x, srcV2.y); - dstAZ = dstFront->ceilingplane.zAt(dstV1.x, dstV1.y); - dstBZ = dstFront->ceilingplane.zAt(dstV2.x, dstV2.y); - } - else // ceiling - { - srcAZ = srcFront->ceilingplane.zAt(srcV1.x, srcV1.y); - srcBZ = srcFront->ceilingplane.zAt(srcV2.x, srcV2.y); - dstAZ = dstFront->floorplane.zAt(dstV1.x, dstV1.y); - dstBZ = dstFront->floorplane.zAt(dstV2.x, dstV2.y); - } - - const vec3 vecSrcA = vec3(vec2(srcV1.x, srcV1.y), srcAZ); - const vec3 vecSrcB = vec3(vec2(srcV2.x, srcV2.y), srcBZ); - const vec3 vecDstA = vec3(vec2(dstV1.x, dstV1.y), dstAZ); - const vec3 vecDstB = vec3(vec2(dstV2.x, dstV2.y), dstBZ); - - // Translation - vec3 originSrc = (vecSrcB + vecSrcA) * 0.5f; - vec3 originDst = (vecDstB + vecDstA) * 0.5f; - - vec3 translation = originDst - originSrc; - - // printf(" Portal translation: %.3f %.3f %.3f\n", translation.x, translation.y, translation.z); - - portal->transformation = mat4::translate(translation); - portal->sourceSectorGroup = srcLine.GetSectorGroup(); - portal->targetSectorGroup = dstLine.GetSectorGroup(); - } - - // Deduplicate portals - auto it = portalCache.find(*portal); - - if (it == portalCache.end()) - { - int id = int(portals.size()); - portalCache.emplace(*portal, id); - portals.push_back(std::move(portal)); - return id; - } - return it->second; -} - -int LevelMesh::GetSampleDistance(const IntSideDef& sidedef, WallPart part) const -{ - auto sampleDistance = sidedef.GetSampleDistance(part); - return sampleDistance ? sampleDistance : defaultSamples; -} - -void LevelMesh::CreateSideSurfaces(FLevel &doomMap, IntSideDef *side) -{ - IntSector *front; - IntSector *back; - - front = doomMap.GetFrontSector(side); - back = doomMap.GetBackSector(side); - - if (front->controlsector) - return; - - FloatVertex v1 = doomMap.GetSegVertex(side->line->v1); - FloatVertex v2 = doomMap.GetSegVertex(side->line->v2); - - if (side->line->sidenum[0] != (ptrdiff_t)(side - &doomMap.Sides[0])) - { - std::swap(v1, v2); - } - - float v1Top = front->ceilingplane.zAt(v1.x, v1.y); - float v1Bottom = front->floorplane.zAt(v1.x, v1.y); - float v2Top = front->ceilingplane.zAt(v2.x, v2.y); - float v2Bottom = front->floorplane.zAt(v2.x, v2.y); - - int typeIndex = side - &doomMap.Sides[0]; - - vec2 dx(v2.x - v1.x, v2.y - v1.y); - float distance = length(dx); - - // line portal - if (side->line->special == Line_SetPortal && side->line->frontsector == front) - { - const unsigned destLineIndex = doomMap.FindFirstLineId(side->line->args[0]); - - if (destLineIndex < doomMap.Lines.Size()) - { - float texWidth = 128.0f; - float texHeight = 128.0f; - - auto surf = std::make_unique(); - surf->material = side->midtexture; - surf->verts.resize(4); - surf->bSky = false; - - surf->verts[0].x = surf->verts[2].x = v1.x; - surf->verts[0].y = surf->verts[2].y = v1.y; - surf->verts[1].x = surf->verts[3].x = v2.x; - surf->verts[1].y = surf->verts[3].y = v2.y; - surf->verts[0].z = v1Bottom; - surf->verts[1].z = v2Bottom; - surf->verts[2].z = v1Top; - surf->verts[3].z = v2Top; - - surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]); - surf->plane.SetDistance(surf->verts[0]); - surf->type = ST_MIDDLESIDE; - surf->typeIndex = typeIndex; - surf->controlSector = nullptr; - surf->sampleDimension = GetSampleDistance(*side, WallPart::MIDDLE); - - float texZ = surf->verts[0].z; - - surf->texUV.resize(4); - surf->texUV[0].x = 0.0f; - surf->texUV[1].x = distance / texWidth; - surf->texUV[2].x = 0.0f; - surf->texUV[3].x = distance / texWidth; - surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight; - surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight; - surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight; - surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight; - - surf->portalDestinationIndex = destLineIndex; - - surf->portalIndex = CreateLinePortal(doomMap, *side->line, doomMap.Lines[destLineIndex]); - - surfaces.push_back(std::move(surf)); - return; - } - else - { - // Warn about broken portal? - } - } - - // line_horizont consumes everything - if (side->line->special == Line_Horizon && front != back) - { - float texWidth = 128.0f; - float texHeight = 128.0f; - - auto surf = std::make_unique(); - surf->material = side->midtexture; - surf->verts.resize(4); - surf->bSky = front->skyFloor || front->skyCeiling; - - surf->verts[0].x = surf->verts[2].x = v1.x; - surf->verts[0].y = surf->verts[2].y = v1.y; - surf->verts[1].x = surf->verts[3].x = v2.x; - surf->verts[1].y = surf->verts[3].y = v2.y; - surf->verts[0].z = v1Bottom; - surf->verts[1].z = v2Bottom; - surf->verts[2].z = v1Top; - surf->verts[3].z = v2Top; - - surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]); - surf->plane.SetDistance(surf->verts[0]); - surf->type = ST_MIDDLESIDE; - surf->typeIndex = typeIndex; - surf->controlSector = nullptr; - surf->sampleDimension = GetSampleDistance(*side, WallPart::MIDDLE); - - float texZ = surf->verts[0].z; - - surf->texUV.resize(4); - surf->texUV[0].x = 0.0f; - surf->texUV[1].x = distance / texWidth; - surf->texUV[2].x = 0.0f; - surf->texUV[3].x = distance / texWidth; - surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight; - surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight; - surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight; - surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight; - - surfaces.push_back(std::move(surf)); - return; - } - - if (back) - { - for (unsigned int j = 0; j < front->x3dfloors.Size(); j++) - { - IntSector *xfloor = front->x3dfloors[j]; - - // Don't create a line when both sectors have the same 3d floor - bool bothSides = false; - for (unsigned int k = 0; k < back->x3dfloors.Size(); k++) - { - if (back->x3dfloors[k] == xfloor) - { - bothSides = true; - break; - } - } - if (bothSides) - continue; - - float texWidth = 128.0f; - float texHeight = 128.0f; - - IntSideDef* otherSide = &doomMap.Sides[side->line->sidenum[0]] == side ? &doomMap.Sides[side->line->sidenum[1]] : &doomMap.Sides[side->line->sidenum[0]]; - - auto surf = std::make_unique(); - surf->material = "texture"; - surf->type = ST_MIDDLESIDE; - surf->typeIndex = typeIndex; - surf->controlSector = xfloor; - surf->sampleDimension = GetSampleDistance(*side, WallPart::MIDDLE); - surf->verts.resize(4); - surf->verts[0].x = surf->verts[2].x = v2.x; - surf->verts[0].y = surf->verts[2].y = v2.y; - surf->verts[1].x = surf->verts[3].x = v1.x; - surf->verts[1].y = surf->verts[3].y = v1.y; - surf->verts[0].z = xfloor->floorplane.zAt(v2.x, v2.y); - surf->verts[1].z = xfloor->floorplane.zAt(v1.x, v1.y); - surf->verts[2].z = xfloor->ceilingplane.zAt(v2.x, v2.y); - surf->verts[3].z = xfloor->ceilingplane.zAt(v1.x, v1.y); - surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]); - surf->plane.SetDistance(surf->verts[0]); - - float texZ = surf->verts[0].z; - - surf->texUV.resize(4); - surf->texUV[0].x = 0.0f; - surf->texUV[1].x = distance / texWidth; - surf->texUV[2].x = 0.0f; - surf->texUV[3].x = distance / texWidth; - surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight; - surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight; - surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight; - surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight; - - surfaces.push_back(std::move(surf)); - } - - float v1TopBack = back->ceilingplane.zAt(v1.x, v1.y); - float v1BottomBack = back->floorplane.zAt(v1.x, v1.y); - float v2TopBack = back->ceilingplane.zAt(v2.x, v2.y); - float v2BottomBack = back->floorplane.zAt(v2.x, v2.y); - - if (v1Top == v1TopBack && v1Bottom == v1BottomBack && v2Top == v2TopBack && v2Bottom == v2BottomBack) - { - return; - } - - // bottom seg - if (v1Bottom < v1BottomBack || v2Bottom < v2BottomBack) - { - bool bSky = false; - - if (front->skyFloor && back->skyFloor) - { - if (front->data.floorheight != back->data.floorheight && side->bottomtexture[0] == '-') - { - bSky = true; - } - } - - if (side->bottomtexture[0] != '-' || bSky) - { - float texWidth = 128.0f; - float texHeight = 128.0f; - - auto surf = std::make_unique(); - surf->material = side->bottomtexture; - surf->verts.resize(4); - - surf->verts[0].x = surf->verts[2].x = v1.x; - surf->verts[0].y = surf->verts[2].y = v1.y; - surf->verts[1].x = surf->verts[3].x = v2.x; - surf->verts[1].y = surf->verts[3].y = v2.y; - surf->verts[0].z = v1Bottom; - surf->verts[1].z = v2Bottom; - surf->verts[2].z = v1BottomBack; - surf->verts[3].z = v2BottomBack; - - surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]); - surf->plane.SetDistance(surf->verts[0]); - surf->type = ST_LOWERSIDE; - surf->typeIndex = typeIndex; - surf->bSky = bSky; - surf->controlSector = nullptr; - surf->sampleDimension = GetSampleDistance(*side, WallPart::BOTTOM); - - float texZ = surf->verts[0].z; - - surf->texUV.resize(4); - surf->texUV[0].x = 0.0f; - surf->texUV[1].x = distance / texWidth; - surf->texUV[2].x = 0.0f; - surf->texUV[3].x = distance / texWidth; - surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight; - surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight; - surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight; - surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight; - - surfaces.push_back(std::move(surf)); - } - - v1Bottom = v1BottomBack; - v2Bottom = v2BottomBack; - } - - // top seg - if (v1Top > v1TopBack || v2Top > v2TopBack) - { - bool bSky = false; - - if (front->skyCeiling && back->skyCeiling) - { - if (front->data.ceilingheight != back->data.ceilingheight) - { - bSky = true; - } - } - - if (side->toptexture[0] != '-' || bSky) - { - float texWidth = 128.0f; - float texHeight = 128.0f; - - auto surf = std::make_unique(); - surf->material = side->toptexture; - surf->verts.resize(4); - - surf->verts[0].x = surf->verts[2].x = v1.x; - surf->verts[0].y = surf->verts[2].y = v1.y; - surf->verts[1].x = surf->verts[3].x = v2.x; - surf->verts[1].y = surf->verts[3].y = v2.y; - surf->verts[0].z = v1TopBack; - surf->verts[1].z = v2TopBack; - surf->verts[2].z = v1Top; - surf->verts[3].z = v2Top; - - surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]); - surf->plane.SetDistance(surf->verts[0]); - surf->type = ST_UPPERSIDE; - surf->typeIndex = typeIndex; - surf->bSky = bSky; - surf->controlSector = nullptr; - surf->sampleDimension = GetSampleDistance(*side, WallPart::TOP); - - float texZ = surf->verts[0].z; - - surf->texUV.resize(4); - surf->texUV[0].x = 0.0f; - surf->texUV[1].x = distance / texWidth; - surf->texUV[2].x = 0.0f; - surf->texUV[3].x = distance / texWidth; - surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight; - surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight; - surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight; - surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight; - - surfaces.push_back(std::move(surf)); - } - - v1Top = v1TopBack; - v2Top = v2TopBack; - } - } - - // middle seg - if (back == nullptr) - { - float texWidth = 128.0f; - float texHeight = 128.0f; - - auto surf = std::make_unique(); - surf->material = side->midtexture; - surf->verts.resize(4); - - surf->verts[0].x = surf->verts[2].x = v1.x; - surf->verts[0].y = surf->verts[2].y = v1.y; - surf->verts[1].x = surf->verts[3].x = v2.x; - surf->verts[1].y = surf->verts[3].y = v2.y; - surf->verts[0].z = v1Bottom; - surf->verts[1].z = v2Bottom; - surf->verts[2].z = v1Top; - surf->verts[3].z = v2Top; - - surf->plane.SetNormal(surf->verts[0], surf->verts[1], surf->verts[2], surf->verts[3]); - surf->plane.SetDistance(surf->verts[0]); - surf->type = ST_MIDDLESIDE; - surf->typeIndex = typeIndex; - surf->controlSector = nullptr; - surf->sampleDimension = GetSampleDistance(*side, WallPart::MIDDLE); - - float texZ = surf->verts[0].z; - - surf->texUV.resize(4); - surf->texUV[0].x = 0.0f; - surf->texUV[1].x = distance / texWidth; - surf->texUV[2].x = 0.0f; - surf->texUV[3].x = distance / texWidth; - surf->texUV[0].y = (surf->verts[0].z - texZ) / texHeight; - surf->texUV[1].y = (surf->verts[1].z - texZ) / texHeight; - surf->texUV[2].y = (surf->verts[2].z - texZ) / texHeight; - surf->texUV[3].y = (surf->verts[3].z - texZ) / texHeight; - - surfaces.push_back(std::move(surf)); - } -} - -void LevelMesh::CreateFloorSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor) -{ - auto surf = std::make_unique(); - surf->sampleDimension = sector->sampleDistanceFloor ? sector->sampleDistanceFloor : defaultSamples; - surf->material = sector->data.floorpic; - surf->verts.resize(sub->numlines); - surf->texUV.resize(sub->numlines); - surf->bSky = sector->skyFloor; - - if (!is3DFloor) - { - surf->plane = sector->floorplane; - surf->portalIndex = CheckAndMakePortal(doomMap, sub, sector, typeIndex, PLANE_FLOOR); - } - else - { - surf->plane = Plane::Inverse(sector->ceilingplane); - } - - for (int j = 0; j < (int)sub->numlines; j++) - { - MapSegGLEx *seg = &doomMap.GLSegs[sub->firstline + (sub->numlines - 1) - j]; - FloatVertex v1 = doomMap.GetSegVertex(seg->v1); - - surf->verts[j].x = v1.x; - surf->verts[j].y = v1.y; - surf->verts[j].z = surf->plane.zAt(surf->verts[j].x, surf->verts[j].y); - - surf->texUV[j].x = v1.x / 64.0f; - surf->texUV[j].y = v1.y / 64.0f; - } - - surf->type = ST_FLOOR; - surf->typeIndex = typeIndex; - surf->controlSector = is3DFloor ? sector : nullptr; - - surfaces.push_back(std::move(surf)); -} - -void LevelMesh::CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor) -{ - auto surf = std::make_unique(); - surf->material = sector->data.ceilingpic; - surf->sampleDimension = sector->sampleDistanceCeiling ? sector->sampleDistanceCeiling : defaultSamples; - surf->verts.resize(sub->numlines); - surf->texUV.resize(sub->numlines); - surf->bSky = sector->skyCeiling; - - if (!is3DFloor) - { - surf->plane = sector->ceilingplane; - surf->portalIndex = CheckAndMakePortal(doomMap, sub, sector, typeIndex, PLANE_CEILING); - } - else - { - surf->plane = Plane::Inverse(sector->floorplane); - } - - for (int j = 0; j < (int)sub->numlines; j++) - { - MapSegGLEx *seg = &doomMap.GLSegs[sub->firstline + j]; - FloatVertex v1 = doomMap.GetSegVertex(seg->v1); - - surf->verts[j].x = v1.x; - surf->verts[j].y = v1.y; - surf->verts[j].z = surf->plane.zAt(surf->verts[j].x, surf->verts[j].y); - - surf->texUV[j].x = v1.x / 64.0f; - surf->texUV[j].y = v1.y / 64.0f; - } - - surf->type = ST_CEILING; - surf->typeIndex = typeIndex; - surf->controlSector = is3DFloor ? sector : nullptr; - - surfaces.push_back(std::move(surf)); -} - -void LevelMesh::CreateSubsectorSurfaces(FLevel &doomMap) -{ - for (int i = 0; i < doomMap.NumGLSubsectors; i++) - { - printf(" Subsectors: %i / %i\r", i + 1, doomMap.NumGLSubsectors); - - MapSubsectorEx *sub = &doomMap.GLSubsectors[i]; - - if (sub->numlines < 3) - { - continue; - } - - IntSector *sector = doomMap.GetSectorFromSubSector(sub); - if (!sector || sector->controlsector) - continue; - - CreateFloorSurface(doomMap, sub, sector, i, false); - CreateCeilingSurface(doomMap, sub, sector, i, false); - - for (unsigned int j = 0; j < sector->x3dfloors.Size(); j++) - { - CreateFloorSurface(doomMap, sub, sector->x3dfloors[j], i, true); - CreateCeilingSurface(doomMap, sub, sector->x3dfloors[j], i, true); - } - } - - printf("\n Leaf surfaces: %i\n", (int)surfaces.size() - doomMap.NumGLSubsectors); -} - -bool LevelMesh::IsDegenerate(const vec3 &v0, const vec3 &v1, const vec3 &v2) -{ - // A degenerate triangle has a zero cross product for two of its sides. - float ax = v1.x - v0.x; - float ay = v1.y - v0.y; - float az = v1.z - v0.z; - float bx = v2.x - v0.x; - float by = v2.y - v0.y; - float bz = v2.z - v0.z; - float crossx = ay * bz - az * by; - float crossy = az * bx - ax * bz; - float crossz = ax * by - ay * bx; - float crosslengthsqr = crossx * crossx + crossy * crossy + crossz * crossz; - return crosslengthsqr <= 1.e-6f; -} - -void LevelMesh::AddLightmapLump(FWadWriter& wadFile) -{ -/* -// LIGHTMAP V2 pseudo-C specification: - -struct LightmapHeader -{ - int version = 2; - uint32_t surfaceCount; - uint32_t pixelCount; - uint32_t uvCount; - SurfaceEntry surfaces[surfaceCount]; - uint16_t pixels[pixelCount * 3]; - float uvs[uvCount * 2]; -}; - -struct SurfaceEntry -{ - uint32_t type, typeIndex; - uint32_t controlSector; // 0xFFFFFFFF is none - uint16_t width, height; // in pixels - uint32_t pixelsOffset; // offset in pixels array - uint32_t uvCount, uvOffset; -}; -*/ - // Calculate size of lump - uint32_t surfaceCount = 0; - uint32_t pixelCount = 0; - uint32_t uvCount = 0; - - for (size_t i = 0; i < surfaces.size(); i++) - { - if (surfaces[i]->atlasPageIndex != -1) - { - surfaceCount++; - pixelCount += surfaces[i]->Area(); - uvCount += surfaces[i]->verts.size(); - - if (surfaces[i]->Area() != surfaces[i]->texPixels.size()) - { - printf("Error: Surface area does not match the pixel count.\n"); - } - } - } - - printf(" Writing %u surfaces out of %llu\n", surfaceCount, surfaces.size()); - - const int version = 2; - - const uint32_t headerSize = sizeof(int) + 3 * sizeof(uint32_t); - const uint32_t bytesPerSurfaceEntry = sizeof(uint32_t) * 6 + sizeof(uint16_t) * 2; - const uint32_t bytesPerPixel = sizeof(uint16_t) * 3; // F16 RGB - const uint32_t bytesPerUV = sizeof(float) * 2; // FVector2 - - uint32_t lumpSize = headerSize + surfaceCount * bytesPerSurfaceEntry + pixelCount * bytesPerPixel + uvCount * bytesPerUV; - - bool debug = false; - - if (debug) - { - printf("Lump size %u bytes\n", lumpSize); - printf("Surfaces: %u\nPixels: %u\nUVs: %u\n", surfaceCount, pixelCount, uvCount); - } - - // Setup buffer - std::vector buffer(lumpSize); - BinFile lumpFile; - lumpFile.SetBuffer(buffer.data()); - - // Write header - lumpFile.Write32(version); - lumpFile.Write32(surfaceCount); - lumpFile.Write32(pixelCount); - lumpFile.Write32(uvCount); - - if (debug) - { - printf("--- Saving surfaces ---\n"); - } - - // Write surfaces - uint32_t pixelsOffset = 0; - uint32_t uvOffset = 0; - - for (size_t i = 0; i < surfaces.size(); i++) - { - const auto* surface = surfaces[i].get(); - - if (surface->atlasPageIndex == -1) - continue; - - lumpFile.Write32(surface->type); - lumpFile.Write32(surface->typeIndex); - lumpFile.Write32(surface->controlSector ? uint32_t(surface->controlSector - &map->Sectors[0]) : 0xffffffff); - - lumpFile.Write16(uint16_t(surface->texWidth)); - lumpFile.Write16(uint16_t(surface->texHeight)); - - lumpFile.Write32(pixelsOffset * 3); - - lumpFile.Write32(surface->lightUV.size()); - lumpFile.Write32(uvOffset); - - pixelsOffset += surface->Area(); - uvOffset += surface->lightUV.size(); - } - - if (debug) - { - printf("--- Saving pixels ---\n"); - } - - // Write surface pixels - for (size_t i = 0; i < surfaces.size(); i++) - { - const auto* surface = surfaces[i].get(); - - if (surface->atlasPageIndex == -1) - continue; - - if (debug) - { - printf("Surface %llu contains %llu pixels\n", i, surface->texPixels.size()); - } - - for (const auto& pixel : surface->texPixels) - { - lumpFile.Write16(floatToHalf(clamp(pixel.r, 0.0f, 65000.0f))); - lumpFile.Write16(floatToHalf(clamp(pixel.g, 0.0f, 65000.0f))); - lumpFile.Write16(floatToHalf(clamp(pixel.b, 0.0f, 65000.0f))); - } - } - - if (debug) - { - printf("--- Saving UVs ---\n"); - } - - // Write normalized texture coordinates - for (size_t i = 0; i < surfaces.size(); i++) - { - const auto* surface = surfaces[i].get(); - - if (surface->atlasPageIndex == -1) - continue; - - if (debug) - { - printf("Surface %llu contains %llu UVs\n", i, surface->verts.size()); - } - - // as of V2 LIGHTMAP version: internal lightmapper uses ZDRay order triangle strips in its internal representation. It will convert from triangle strips to triangle fan on its own. - - int count = surface->verts.size(); - if (surface->type == ST_FLOOR || surface->type == ST_CEILING) - { - for (int j = 0; j < count; j++) - { - lumpFile.WriteFloat(surface->lightUV[j].x); - lumpFile.WriteFloat(surface->lightUV[j].y); - } - } - else - { - lumpFile.WriteFloat(surface->lightUV[0].x); - lumpFile.WriteFloat(surface->lightUV[0].y); - - lumpFile.WriteFloat(surface->lightUV[1].x); - lumpFile.WriteFloat(surface->lightUV[1].y); - - lumpFile.WriteFloat(surface->lightUV[2].x); - lumpFile.WriteFloat(surface->lightUV[2].y); - - lumpFile.WriteFloat(surface->lightUV[3].x); - lumpFile.WriteFloat(surface->lightUV[3].y); - } - } - - // Compress and store in lump - ZLibOut zout(wadFile); - wadFile.StartWritingLump("LIGHTMAP"); - zout.Write(buffer.data(), lumpFile.BufferAt() - lumpFile.Buffer()); -} - -void LevelMesh::Export(std::string filename) -{ - printf(" Exporting mesh \"%s\"\n", filename.c_str()); - - // This is so ugly! I had nothing to do with it! ;) - std::string mtlfilename = filename; - for (int i = 0; i < 3; i++) mtlfilename.pop_back(); - mtlfilename += "mtl"; - - TArray outvertices; - TArray outuv; - TArray outnormal; - TArray outface; - TArray outLightmapId; - - outvertices.Resize(MeshVertices.Size()); - outuv.Resize(MeshVertices.Size()); - outnormal.Resize(MeshVertices.Size()); - outLightmapId.Resize(MeshElements.Size() / 3); - - for (unsigned int surfidx = 0; surfidx < MeshElements.Size() / 3; surfidx++) - { - Surface* surface = surfaces[MeshSurfaces[surfidx]].get(); - - outLightmapId[surfidx] = surface->atlasPageIndex; - - for (int i = 0; i < 3; i++) - { - int elementidx = surfidx * 3 + i; - int vertexidx = MeshElements[elementidx]; - int uvindex = MeshUVIndex[vertexidx]; - - outvertices[vertexidx] = MeshVertices[vertexidx]; - outuv[vertexidx] = surface->lightUV[uvindex]; - outnormal[vertexidx] = surface->plane.Normal(); - outface.Push(vertexidx); - } - } - - std::string buffer; - buffer.reserve(16 * 1024 * 1024); - - buffer += "# zdray exported mesh\r\n"; - - buffer += "mtllib "; - buffer += mtlfilename; - buffer += "\r\n"; - - float scale = 0.01f; - - for (unsigned int i = 0; i < outvertices.Size(); i++) - { - buffer += "v "; - buffer += std::to_string(-outvertices[i].x * scale); - buffer += " "; - buffer += std::to_string(outvertices[i].z * scale); - buffer += " "; - buffer += std::to_string(outvertices[i].y * scale); - buffer += "\r\n"; - } - - for (unsigned int i = 0; i < outnormal.Size(); i++) - { - buffer += "vn "; - buffer += std::to_string(-outnormal[i].x); - buffer += " "; - buffer += std::to_string(outnormal[i].z); - buffer += " "; - buffer += std::to_string(outnormal[i].y); - buffer += "\r\n"; - } - - for (unsigned int i = 0; i < outuv.Size(); i++) - { - buffer += "vt "; - buffer += std::to_string(outuv[i].x); - buffer += " "; - buffer += std::to_string(1.0f - outuv[i].y); - buffer += "\r\n"; - } - - int prevLightmap = -1; - - for (unsigned int i = 0; i < outface.Size(); i += 3) - { - if (prevLightmap != outLightmapId[i/3]) - { - prevLightmap = outLightmapId[i/3]; - - if (prevLightmap < 0) - { - buffer += "usemtl lightmap_none\r\n"; - } - else - { - buffer += "usemtl lightmap" + std::to_string(prevLightmap) + "\r\n"; - } - } - - std::string e0 = std::to_string(outface[i] + 1); - std::string e1 = std::to_string(outface[i + 1] + 1); - std::string e2 = std::to_string(outface[i + 2] + 1); - buffer += "f "; - buffer += e0; - buffer += "/"; - buffer += e0; - buffer += "/"; - buffer += e0; - buffer += " "; - buffer += e1; - buffer += "/"; - buffer += e1; - buffer += "/"; - buffer += e1; - buffer += " "; - buffer += e2; - buffer += "/"; - buffer += e2; - buffer += "/"; - buffer += e2; - buffer += "\r\n"; - } - - FILE* file = fopen(filename.c_str(), "wb"); - if (file) - { - fwrite(buffer.data(), buffer.size(), 1, file); - fclose(file); - } - - std::string mtl = "newtml lightmap_none\r\n" - "Ka 0 0 0\r\n" - "Kd 0 0 0\r\n" - "ks 0 0 0\r\n" - "map_Kd lightmap0.png\r\n"; - - for (int i = 0; i < textures.size(); i++) - { - mtl += "\r\nnewmtl lightmap" + std::to_string(i) + "\r\n"; - mtl += - "Ka 1 1 1\r\n" - "Kd 1 1 1\r\n" - "Ks 0 0 0\r\n"; - mtl += "map_Ka lightmap" + std::to_string(i) + ".png\r\n"; - mtl += "map_Kd lightmap" + std::to_string(i) + ".png\r\n"; - } - - file = fopen(mtlfilename.c_str(), "wb"); - if (file) - { - fwrite(mtl.data(), mtl.size(), 1, file); - fclose(file); - } - - int index = 0; - for (const auto& texture : textures) - { - int w = texture->Width(); - int h = texture->Height(); - uint16_t* p = texture->Pixels(); -#if 1 - std::vector buf(w * h * 4); - uint8_t* buffer = buf.data(); - for (int i = 0; i < w * h; i++) - { - buffer[i * 4] = (uint8_t)(int)clamp(halfToFloat(p[i * 3]) * 255.0f, 0.0f, 255.0f); - buffer[i * 4 + 1] = (uint8_t)(int)clamp(halfToFloat(p[i * 3 + 1]) * 255.0f, 0.0f, 255.0f); - buffer[i * 4 + 2] = (uint8_t)(int)clamp(halfToFloat(p[i * 3 + 2]) * 255.0f, 0.0f, 255.0f); - buffer[i * 4 + 3] = 0xff; - } - PNGWriter::save("lightmap" + std::to_string(index++) + ".png", w, h, 4, buffer); -#else - std::vector buf(w * h * 4); - uint16_t* buffer = buf.data(); - for (int i = 0; i < w * h; i++) - { - buffer[i * 4] = (uint16_t)(int)clamp(halfToFloat(p[i * 3]) * 65535.0f, 0.0f, 65535.0f); - buffer[i * 4 + 1] = (uint16_t)(int)clamp(halfToFloat(p[i * 3 + 1]) * 65535.0f, 0.0f, 65535.0f); - buffer[i * 4 + 2] = (uint16_t)(int)clamp(halfToFloat(p[i * 3 + 2]) * 65535.0f, 0.0f, 65535.0f); - buffer[i * 4 + 3] = 0xffff; - } - PNGWriter::save("lightmap" + std::to_string(index++) + ".png", w, h, 8, buffer); -#endif - } - - printf(" Export complete\n"); -} diff --git a/src/lightmap/levelmesh.h b/src/lightmap/levelmesh.h deleted file mode 100644 index 8c4bfb4..0000000 --- a/src/lightmap/levelmesh.h +++ /dev/null @@ -1,195 +0,0 @@ -//----------------------------------------------------------------------------- -// Note: this is a modified version of dlight. It is not the original software. -//----------------------------------------------------------------------------- -// -// Copyright (c) 2013-2014 Samuel Villarreal -// svkaiser@gmail.com -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "framework/tarray.h" -#include "framework/halffloat.h" -#include "lightmaptexture.h" -#include "math/mathlib.h" -#include "collision.h" -#include "portal.h" - -#include "dp_rect_pack/dp_rect_pack.h" - -typedef dp::rect_pack::RectPacker RectPacker; - -struct MapSubsectorEx; -struct IntSector; -struct IntSideDef; -struct IntLineDef; -struct FLevel; -struct ThingLight; -class FWadWriter; - -enum class WallPart; - -enum SurfaceType -{ - ST_UNKNOWN, - ST_MIDDLESIDE, - ST_UPPERSIDE, - ST_LOWERSIDE, - ST_CEILING, - ST_FLOOR -}; - -struct Surface -{ - // Surface geometry - SurfaceType type = ST_UNKNOWN; - std::vector verts; - Plane plane; - BBox bounds; - - // Surface material - std::string material; - std::vector texUV; - - // Surface properties - int typeIndex = 0; - IntSector* controlSector = nullptr; - int sampleDimension = 0; - bool bSky = false; - - // Portal - int portalDestinationIndex = -1; // line or sector index - int portalIndex = -1; - - // Sector group - int sectorGroup = 0; - - // Touching light sources - std::vector LightList; - - // Lightmap world coordinates for the texture - vec3 worldOrigin = { 0.0f }; - vec3 worldStepX = { 0.0f }; - vec3 worldStepY = { 0.0f }; - - // Calculate world coordinates to UV coordinates - vec3 translateWorldToLocal = { 0.0f }; - vec3 projLocalToU = { 0.0f }; - vec3 projLocalToV = { 0.0f }; - - // Output lightmap for the surface - int texWidth = 0; - int texHeight = 0; - std::vector texPixels; - - inline uint32_t Area() const { return uint32_t(texWidth * texHeight); } - - // UV coordinates for the vertices - std::vector lightUV; - - // Placement in final texture atlas - int atlasPageIndex = -1; - int atlasX = 0; - int atlasY = 0; - - // Smoothing group surface is to be rendered with - int smoothingGroupIndex = -1; -}; - -struct SmoothingGroup -{ - Plane plane = Plane(0, 0, 1, 0); - int sectorGroup = 0; - std::vector surfaces; -}; - -class LevelMesh -{ -public: - LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize); - - void CreateTextures(); - void AddLightmapLump(FWadWriter& wadFile); - void Export(std::string filename); - - FLevel* map = nullptr; - - std::vector> surfaces; - - std::vector> textures; - - std::vector smoothingGroups; - - std::vector> portals; - - int defaultSamples = 16; - int textureWidth = 128; - int textureHeight = 128; - - TArray MeshVertices; - TArray MeshUVIndex; - TArray MeshElements; - TArray MeshSurfaces; - - std::unique_ptr Collision; - -private: - // Portal to portals[] index - std::map portalCache; - - // Portal lights - std::vector> portalLights; - std::set touchedPortals; - int lightPropagationRecursiveDepth = 0; - - void CreateSubsectorSurfaces(FLevel &doomMap); - void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor); - void CreateFloorSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor); - void CreateSideSurfaces(FLevel &doomMap, IntSideDef *side); - - void BuildSurfaceParams(Surface* surface); - BBox GetBoundsFromSurface(const Surface* surface); - - void BuildLightLists(FLevel &doomMap); - void PropagateLight(FLevel& doomMap, ThingLight* thing); - - void BuildSmoothingGroups(FLevel& doomMap); - - void BlurSurfaces(); - void FinishSurface(RectPacker& packer, Surface* surface); - - static bool IsDegenerate(const vec3 &v0, const vec3 &v1, const vec3 &v2); - - int CheckAndMakePortal(FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, int typeIndex, int plane); - - int CreateLinePortal(FLevel &doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine); - int CreatePlanePortal(FLevel &doomMap, const IntLineDef& srcLine, const IntLineDef& dstLine); - - int GetSampleDistance(const IntSideDef& sidedef, WallPart part) const; -}; diff --git a/src/lightmap/lightmaptexture.cpp b/src/lightmap/lightmaptexture.cpp deleted file mode 100644 index b625295..0000000 --- a/src/lightmap/lightmaptexture.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -#include "lightmaptexture.h" -#include "framework/halffloat.h" -#include - -LightmapTexture::LightmapTexture(int width, int height) : textureWidth(width), textureHeight(height) -{ -#ifdef _DEBUG - mPixels.resize(width * height * 3, floatToHalf(0.5f)); -#else - mPixels.resize(width * height * 3, 0); -#endif -} diff --git a/src/lightmap/lightmaptexture.h b/src/lightmap/lightmaptexture.h deleted file mode 100644 index b3613ad..0000000 --- a/src/lightmap/lightmaptexture.h +++ /dev/null @@ -1,20 +0,0 @@ - -#pragma once - -#include -#include - -class LightmapTexture -{ -public: - LightmapTexture(int width, int height); - - int Width() const { return textureWidth; } - int Height() const { return textureHeight; } - uint16_t* Pixels() { return mPixels.data(); } - -private: - int textureWidth; - int textureHeight; - std::vector mPixels; -}; diff --git a/src/lightmap/pngwriter.cpp b/src/lightmap/pngwriter.cpp deleted file mode 100644 index dda40c4..0000000 --- a/src/lightmap/pngwriter.cpp +++ /dev/null @@ -1,218 +0,0 @@ - -#include "math/mathlib.h" -#include "pngwriter.h" -#include "framework/binfile.h" -#include "framework/templates.h" -#include "framework/halffloat.h" -#include -#include -#include -#include -#include -#include -#include -#include - -void PNGWriter::save(const std::string& filename, int width, int height, int bytes_per_pixel, void* pixels) -{ - PNGImage image; - image.width = width; - image.height = height; - image.bytes_per_pixel = bytes_per_pixel; - image.pixel_ratio = 1.0f; - image.data = pixels; - - FILE *file = fopen(filename.c_str(), "wb"); - if (file) - { - PNGWriter writer; - writer.file = file; - writer.image = ℑ - writer.write_magic(); - writer.write_headers(); - writer.write_data(); - writer.write_chunk("IEND", nullptr, 0); - fclose(file); - } -} - -void PNGWriter::write_magic() -{ - unsigned char png_magic[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; - write(png_magic, 8); -} - -void PNGWriter::write_headers() -{ - int ppm = (int)std::round(3800 * image->pixel_ratio); - int ppm_x = ppm; - int ppm_y = ppm; - - int width = image->width; - int height = image->height; - int bit_depth = image->bytes_per_pixel == 8 ? 16 : 8; - int color_type = 6; - int compression_method = 0; - int filter_method = 0; - int interlace_method = 0; - - unsigned char idhr[13]; - idhr[0] = (width >> 24) & 0xff; - idhr[1] = (width >> 16) & 0xff; - idhr[2] = (width >> 8) & 0xff; - idhr[3] = width & 0xff; - idhr[4] = (height >> 24) & 0xff; - idhr[5] = (height >> 16) & 0xff; - idhr[6] = (height >> 8) & 0xff; - idhr[7] = height & 0xff; - idhr[8] = bit_depth; - idhr[9] = color_type; - idhr[10] = compression_method; - idhr[11] = filter_method; - idhr[12] = interlace_method; - - //unsigned char srgb[1]; - //srgb[0] = 0; - - unsigned char phys[9]; - phys[0] = (ppm_x >> 24) & 0xff; - phys[1] = (ppm_x >> 16) & 0xff; - phys[2] = (ppm_x >> 8) & 0xff; - phys[3] = ppm_x & 0xff; - phys[4] = (ppm_y >> 24) & 0xff; - phys[5] = (ppm_y >> 16) & 0xff; - phys[6] = (ppm_y >> 8) & 0xff; - phys[7] = ppm_y & 0xff; - phys[8] = 1; // pixels per meter - - write_chunk("IHDR", idhr, 13); - - if (ppm != 0) - write_chunk("pHYs", phys, 9); - - //write_chunk("sRGB", srgb, 1); -} - -void PNGWriter::write_data() -{ - //int width = image->width; - int height = image->height; - int bytes_per_pixel = image->bytes_per_pixel; - int pitch = image->width * bytes_per_pixel; - - std::vector scanline_orig; - std::vector scanline_filtered; - scanline_orig.resize((image->width + 1) * bytes_per_pixel); - scanline_filtered.resize(image->width * bytes_per_pixel + 1); - - auto idat_uncompressed = std::make_shared((int)(height * scanline_filtered.size())); - - for (int y = 0; y < height; y++) - { - // Grab scanline - memcpy(scanline_orig.data() + bytes_per_pixel, (uint8_t*)image->data + y * pitch, scanline_orig.size() - bytes_per_pixel); - - // Convert to big endian for 16 bit - if (bytes_per_pixel == 8) - { - for (size_t x = 0; x < scanline_orig.size(); x += 2) - { - std::swap(scanline_orig[x], scanline_orig[x + 1]); - } - } - - // Filter scanline - /* - scanline_filtered[0] = 0; // None filter type - for (int i = bytes_per_pixel; i < scanline_orig.size(); i++) - { - scanline_filtered[i - bytes_per_pixel + 1] = scanline_orig[i]; - } - */ - scanline_filtered[0] = 1; // Sub filter type - for (int i = bytes_per_pixel; i < scanline_orig.size(); i++) - { - unsigned char a = scanline_orig[i - bytes_per_pixel]; - unsigned char x = scanline_orig[i]; - scanline_filtered[i - bytes_per_pixel + 1] = x - a; - } - - // Output scanline - memcpy((uint8_t*)idat_uncompressed->data + y * scanline_filtered.size(), scanline_filtered.data(), scanline_filtered.size()); - } - - auto idat = std::make_unique(idat_uncompressed->size * 125 / 100); - idat->size = (int)compressdata(idat.get(), idat_uncompressed.get(), false); - - write_chunk("IDAT", idat->data, (int)idat->size); -} - -void PNGWriter::write_chunk(const char name[4], const void *data, int size) -{ - unsigned char size_data[4]; - size_data[0] = (size >> 24) & 0xff; - size_data[1] = (size >> 16) & 0xff; - size_data[2] = (size >> 8) & 0xff; - size_data[3] = size & 0xff; - write(size_data, 4); - - write(name, 4); - - write(data, size); - unsigned int crc32 = PNGCRC32::crc(name, data, size); - - unsigned char crc32_data[4]; - crc32_data[0] = (crc32 >> 24) & 0xff; - crc32_data[1] = (crc32 >> 16) & 0xff; - crc32_data[2] = (crc32 >> 8) & 0xff; - crc32_data[3] = crc32 & 0xff; - write(crc32_data, 4); -} - -void PNGWriter::write(const void *data, int size) -{ - fwrite(data, size, 1, file); -} - -size_t PNGWriter::compressdata(DataBuffer *out, const DataBuffer *data, bool raw) -{ - if (data->size > (size_t)0xffffffff || out->size > (size_t)0xffffffff) - throw std::runtime_error("Data is too big"); - - const int window_bits = 15; - - int compression_level = 6; - int strategy = Z_DEFAULT_STRATEGY; - - z_stream zs; - memset(&zs, 0, sizeof(z_stream)); - int result = deflateInit2(&zs, compression_level, Z_DEFLATED, raw ? -window_bits : window_bits, 8, strategy); // Undocumented: if wbits is negative, zlib skips header check - if (result != Z_OK) - throw std::runtime_error("Zlib deflateInit failed"); - - zs.next_in = (unsigned char *)data->data; - zs.avail_in = (unsigned int)data->size; - zs.next_out = (unsigned char *)out->data; - zs.avail_out = (unsigned int)out->size; - - size_t outSize = 0; - try - { - int result = deflate(&zs, Z_FINISH); - if (result == Z_NEED_DICT) throw std::runtime_error("Zlib deflate wants a dictionary!"); - if (result == Z_DATA_ERROR) throw std::runtime_error("Zip data stream is corrupted"); - if (result == Z_STREAM_ERROR) throw std::runtime_error("Zip stream structure was inconsistent!"); - if (result == Z_MEM_ERROR) throw std::runtime_error("Zlib did not have enough memory to compress file!"); - if (result == Z_BUF_ERROR) throw std::runtime_error("Not enough data in buffer when Z_FINISH was used"); - if (result != Z_STREAM_END) throw std::runtime_error("Zlib deflate failed while compressing zip file!"); - outSize = zs.total_out; - } - catch (...) - { - deflateEnd(&zs); - throw; - } - deflateEnd(&zs); - - return outSize; -} diff --git a/src/lightmap/pngwriter.h b/src/lightmap/pngwriter.h deleted file mode 100644 index 681b0f2..0000000 --- a/src/lightmap/pngwriter.h +++ /dev/null @@ -1,78 +0,0 @@ - -#pragma once - -#include - -class PNGWriter -{ -public: - static void save(const std::string& filename, int width, int height, int bytes_per_pixel, void* pixels); - - struct DataBuffer - { - DataBuffer(int size) : size(size) { data = new uint8_t[size]; } - ~DataBuffer() { delete[] data; } - int size; - uint8_t* data; - }; - -private: - struct PNGImage - { - int width; - int height; - int bytes_per_pixel; - void* data; - float pixel_ratio; - }; - - const PNGImage* image; - FILE* file; - - class PNGCRC32 - { - public: - static unsigned long crc(const char name[4], const void* data, int len) - { - static PNGCRC32 impl; - - const unsigned char* buf = reinterpret_cast(data); - - unsigned int c = 0xffffffff; - - for (int n = 0; n < 4; n++) - c = impl.crc_table[(c ^ name[n]) & 0xff] ^ (c >> 8); - - for (int n = 0; n < len; n++) - c = impl.crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); - - return c ^ 0xffffffff; - } - - private: - unsigned int crc_table[256]; - - PNGCRC32() - { - for (unsigned int n = 0; n < 256; n++) - { - unsigned int c = n; - for (unsigned int k = 0; k < 8; k++) - { - if ((c & 1) == 1) - c = 0xedb88320 ^ (c >> 1); - else - c = c >> 1; - } - crc_table[n] = c; - } - } - }; - - void write_magic(); - void write_headers(); - void write_data(); - void write_chunk(const char name[4], const void* data, int size); - void write(const void* data, int size); - size_t compressdata(DataBuffer* out, const DataBuffer* data, bool raw); -}; diff --git a/src/lightmap/portal.h b/src/lightmap/portal.h deleted file mode 100644 index 1adcbe9..0000000 --- a/src/lightmap/portal.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "math/mathlib.h" - -struct Portal -{ - mat4 transformation = mat4::identity(); - int sourceSectorGroup = 0; - int targetSectorGroup = 0; - - inline vec3 TransformPosition(const vec3& pos) const - { - auto v = transformation * vec4(pos, 1.0); - return vec3(v.x, v.y, v.z); - } - - inline vec3 TransformRotation(const vec3& dir) const - { - auto v = transformation * vec4(dir, 0.0); - return vec3(v.x, v.y, v.z); - } - - // Checks only transformation - inline bool IsInverseTransformationPortal(const Portal& portal) const - { - auto diff = portal.TransformPosition(TransformPosition(vec3(0))); - return abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001; - } - - // Checks only transformation - inline bool IsEqualTransformationPortal(const Portal& portal) const - { - auto diff = portal.TransformPosition(vec3(0)) - TransformPosition(vec3(0)); - return (abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001); - } - - - // Checks transformation, source and destiantion sector groups - inline bool IsEqualPortal(const Portal& portal) const - { - return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup && IsEqualTransformationPortal(portal); - } - - // Checks transformation, source and destiantion sector groups - inline bool IsInversePortal(const Portal& portal) const - { - return sourceSectorGroup == portal.targetSectorGroup && targetSectorGroup == portal.sourceSectorGroup && IsInverseTransformationPortal(portal); - } - - inline void DumpInfo() - { - auto v = TransformPosition(vec3(0)); - printf("Portal offset: %.3f %.3f %.3f\n\tsource group:\t%d\n\ttarget group:\t%d", v.x, v.y, v.z, sourceSectorGroup, targetSectorGroup); - } -}; - -// for use with std::set to recursively go through portals and skip returning portals -struct RecursivePortalComparator -{ - bool operator()(const Portal& a, const Portal& b) const - { - return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; - } -}; - -// for use with std::map to reject portals which have the same effect for light rays -struct IdenticalPortalComparator -{ - bool operator()(const Portal& a, const Portal& b) const - { - return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0; - } -}; diff --git a/src/lightmap/renderdoc_app.h b/src/lightmap/renderdoc_app.h deleted file mode 100644 index ca5a79b..0000000 --- a/src/lightmap/renderdoc_app.h +++ /dev/null @@ -1,692 +0,0 @@ -/****************************************************************************** - * The MIT License (MIT) - * - * Copyright (c) 2019-2021 Baldur Karlsson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -#pragma once - -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html -// - -#if !defined(RENDERDOC_NO_STDINT) -#include -#endif - -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define RENDERDOC_CC __cdecl -#elif defined(__linux__) -#define RENDERDOC_CC -#elif defined(__APPLE__) -#define RENDERDOC_CC -#else -#error "Unknown platform" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////// -// Constants not used directly in below API - -// This is a GUID/magic value used for when applications pass a path where shader debug -// information can be found to match up with a stripped shader. -// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue = -// RENDERDOC_ShaderDebugMagicValue_value -#define RENDERDOC_ShaderDebugMagicValue_struct \ - { \ - 0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \ - } - -// as an alternative when you want a byte array (assuming x86 endianness): -#define RENDERDOC_ShaderDebugMagicValue_bytearray \ - { \ - 0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \ - } - -// truncated version when only a uint64_t is available (e.g. Vulkan tags): -#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL - -////////////////////////////////////////////////////////////////////////////////////////////////// -// RenderDoc capture options -// - -typedef enum RENDERDOC_CaptureOption { - // Allow the application to enable vsync - // - // Default - enabled - // - // 1 - The application can enable or disable vsync at will - // 0 - vsync is force disabled - eRENDERDOC_Option_AllowVSync = 0, - - // Allow the application to enable fullscreen - // - // Default - enabled - // - // 1 - The application can enable or disable fullscreen at will - // 0 - fullscreen is force disabled - eRENDERDOC_Option_AllowFullscreen = 1, - - // Record API debugging events and messages - // - // Default - disabled - // - // 1 - Enable built-in API debugging features and records the results into - // the capture, which is matched up with events on replay - // 0 - no API debugging is forcibly enabled - eRENDERDOC_Option_APIValidation = 2, - eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum - - // Capture CPU callstacks for API events - // - // Default - disabled - // - // 1 - Enables capturing of callstacks - // 0 - no callstacks are captured - eRENDERDOC_Option_CaptureCallstacks = 3, - - // When capturing CPU callstacks, only capture them from actions. - // This option does nothing without the above option being enabled - // - // Default - disabled - // - // 1 - Only captures callstacks for actions. - // Ignored if CaptureCallstacks is disabled - // 0 - Callstacks, if enabled, are captured for every event. - eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4, - eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4, - - // Specify a delay in seconds to wait for a debugger to attach, after - // creating or injecting into a process, before continuing to allow it to run. - // - // 0 indicates no delay, and the process will run immediately after injection - // - // Default - 0 seconds - // - eRENDERDOC_Option_DelayForDebugger = 5, - - // Verify buffer access. This includes checking the memory returned by a Map() call to - // detect any out-of-bounds modification, as well as initialising buffers with undefined contents - // to a marker value to catch use of uninitialised memory. - // - // NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do - // not do the same kind of interception & checking and undefined contents are really undefined. - // - // Default - disabled - // - // 1 - Verify buffer access - // 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in - // RenderDoc. - eRENDERDOC_Option_VerifyBufferAccess = 6, - - // The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites. - // This option now controls the filling of uninitialised buffers with 0xdddddddd which was - // previously always enabled - eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess, - - // Hooks any system API calls that create child processes, and injects - // RenderDoc into them recursively with the same options. - // - // Default - disabled - // - // 1 - Hooks into spawned child processes - // 0 - Child processes are not hooked by RenderDoc - eRENDERDOC_Option_HookIntoChildren = 7, - - // By default RenderDoc only includes resources in the final capture necessary - // for that frame, this allows you to override that behaviour. - // - // Default - disabled - // - // 1 - all live resources at the time of capture are included in the capture - // and available for inspection - // 0 - only the resources referenced by the captured frame are included - eRENDERDOC_Option_RefAllResources = 8, - - // **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or - // getting it will be ignored, to allow compatibility with older versions. - // In v1.1 the option acts as if it's always enabled. - // - // By default RenderDoc skips saving initial states for resources where the - // previous contents don't appear to be used, assuming that writes before - // reads indicate previous contents aren't used. - // - // Default - disabled - // - // 1 - initial contents at the start of each captured frame are saved, even if - // they are later overwritten or cleared before being used. - // 0 - unless a read is detected, initial contents will not be saved and will - // appear as black or empty data. - eRENDERDOC_Option_SaveAllInitials = 9, - - // In APIs that allow for the recording of command lists to be replayed later, - // RenderDoc may choose to not capture command lists before a frame capture is - // triggered, to reduce overheads. This means any command lists recorded once - // and replayed many times will not be available and may cause a failure to - // capture. - // - // NOTE: This is only true for APIs where multithreading is difficult or - // discouraged. Newer APIs like Vulkan and D3D12 will ignore this option - // and always capture all command lists since the API is heavily oriented - // around it and the overheads have been reduced by API design. - // - // 1 - All command lists are captured from the start of the application - // 0 - Command lists are only captured if their recording begins during - // the period when a frame capture is in progress. - eRENDERDOC_Option_CaptureAllCmdLists = 10, - - // Mute API debugging output when the API validation mode option is enabled - // - // Default - enabled - // - // 1 - Mute any API debug messages from being displayed or passed through - // 0 - API debugging is displayed as normal - eRENDERDOC_Option_DebugOutputMute = 11, - - // Option to allow vendor extensions to be used even when they may be - // incompatible with RenderDoc and cause corrupted replays or crashes. - // - // Default - inactive - // - // No values are documented, this option should only be used when absolutely - // necessary as directed by a RenderDoc developer. - eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12, - -} RENDERDOC_CaptureOption; - -// Sets an option that controls how RenderDoc behaves on capture. -// -// Returns 1 if the option and value are valid -// Returns 0 if either is invalid and the option is unchanged -typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val); -typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val); - -// Gets the current value of an option as a uint32_t -// -// If the option is invalid, 0xffffffff is returned -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt); - -// Gets the current value of an option as a float -// -// If the option is invalid, -FLT_MAX is returned -typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt); - -typedef enum RENDERDOC_InputButton { - // '0' - '9' matches ASCII values - eRENDERDOC_Key_0 = 0x30, - eRENDERDOC_Key_1 = 0x31, - eRENDERDOC_Key_2 = 0x32, - eRENDERDOC_Key_3 = 0x33, - eRENDERDOC_Key_4 = 0x34, - eRENDERDOC_Key_5 = 0x35, - eRENDERDOC_Key_6 = 0x36, - eRENDERDOC_Key_7 = 0x37, - eRENDERDOC_Key_8 = 0x38, - eRENDERDOC_Key_9 = 0x39, - - // 'A' - 'Z' matches ASCII values - eRENDERDOC_Key_A = 0x41, - eRENDERDOC_Key_B = 0x42, - eRENDERDOC_Key_C = 0x43, - eRENDERDOC_Key_D = 0x44, - eRENDERDOC_Key_E = 0x45, - eRENDERDOC_Key_F = 0x46, - eRENDERDOC_Key_G = 0x47, - eRENDERDOC_Key_H = 0x48, - eRENDERDOC_Key_I = 0x49, - eRENDERDOC_Key_J = 0x4A, - eRENDERDOC_Key_K = 0x4B, - eRENDERDOC_Key_L = 0x4C, - eRENDERDOC_Key_M = 0x4D, - eRENDERDOC_Key_N = 0x4E, - eRENDERDOC_Key_O = 0x4F, - eRENDERDOC_Key_P = 0x50, - eRENDERDOC_Key_Q = 0x51, - eRENDERDOC_Key_R = 0x52, - eRENDERDOC_Key_S = 0x53, - eRENDERDOC_Key_T = 0x54, - eRENDERDOC_Key_U = 0x55, - eRENDERDOC_Key_V = 0x56, - eRENDERDOC_Key_W = 0x57, - eRENDERDOC_Key_X = 0x58, - eRENDERDOC_Key_Y = 0x59, - eRENDERDOC_Key_Z = 0x5A, - - // leave the rest of the ASCII range free - // in case we want to use it later - eRENDERDOC_Key_NonPrintable = 0x100, - - eRENDERDOC_Key_Divide, - eRENDERDOC_Key_Multiply, - eRENDERDOC_Key_Subtract, - eRENDERDOC_Key_Plus, - - eRENDERDOC_Key_F1, - eRENDERDOC_Key_F2, - eRENDERDOC_Key_F3, - eRENDERDOC_Key_F4, - eRENDERDOC_Key_F5, - eRENDERDOC_Key_F6, - eRENDERDOC_Key_F7, - eRENDERDOC_Key_F8, - eRENDERDOC_Key_F9, - eRENDERDOC_Key_F10, - eRENDERDOC_Key_F11, - eRENDERDOC_Key_F12, - - eRENDERDOC_Key_Home, - eRENDERDOC_Key_End, - eRENDERDOC_Key_Insert, - eRENDERDOC_Key_Delete, - eRENDERDOC_Key_PageUp, - eRENDERDOC_Key_PageDn, - - eRENDERDOC_Key_Backspace, - eRENDERDOC_Key_Tab, - eRENDERDOC_Key_PrtScrn, - eRENDERDOC_Key_Pause, - - eRENDERDOC_Key_Max, -} RENDERDOC_InputButton; - -// Sets which key or keys can be used to toggle focus between multiple windows -// -// If keys is NULL or num is 0, toggle keys will be disabled -typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num); - -// Sets which key or keys can be used to capture the next frame -// -// If keys is NULL or num is 0, captures keys will be disabled -typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num); - -typedef enum RENDERDOC_OverlayBits { - // This single bit controls whether the overlay is enabled or disabled globally - eRENDERDOC_Overlay_Enabled = 0x1, - - // Show the average framerate over several seconds as well as min/max - eRENDERDOC_Overlay_FrameRate = 0x2, - - // Show the current frame number - eRENDERDOC_Overlay_FrameNumber = 0x4, - - // Show a list of recent captures, and how many captures have been made - eRENDERDOC_Overlay_CaptureList = 0x8, - - // Default values for the overlay mask - eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate | - eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList), - - // Enable all bits - eRENDERDOC_Overlay_All = ~0U, - - // Disable all bits - eRENDERDOC_Overlay_None = 0, -} RENDERDOC_OverlayBits; - -// returns the overlay bits that have been set -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)(); -// sets the overlay bits with an and & or mask -typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or); - -// this function will attempt to remove RenderDoc's hooks in the application. -// -// Note: that this can only work correctly if done immediately after -// the module is loaded, before any API work happens. RenderDoc will remove its -// injected hooks and shut down. Behaviour is undefined if this is called -// after any API functions have been called, and there is still no guarantee of -// success. -typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)(); - -// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers. -typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown; - -// This function will unload RenderDoc's crash handler. -// -// If you use your own crash handler and don't want RenderDoc's handler to -// intercede, you can call this function to unload it and any unhandled -// exceptions will pass to the next handler. -typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)(); - -// Sets the capture file path template -// -// pathtemplate is a UTF-8 string that gives a template for how captures will be named -// and where they will be saved. -// -// Any extension is stripped off the path, and captures are saved in the directory -// specified, and named with the filename and the frame number appended. If the -// directory does not exist it will be created, including any parent directories. -// -// If pathtemplate is NULL, the template will remain unchanged -// -// Example: -// -// SetCaptureFilePathTemplate("my_captures/example"); -// -// Capture #1 -> my_captures/example_frame123.rdc -// Capture #2 -> my_captures/example_frame456.rdc -typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate); - -// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string -typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)(); - -// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers. -typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate; -typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate; - -// returns the number of captures that have been made -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)(); - -// This function returns the details of a capture, by index. New captures are added -// to the end of the list. -// -// filename will be filled with the absolute path to the capture file, as a UTF-8 string -// pathlength will be written with the length in bytes of the filename string -// timestamp will be written with the time of the capture, in seconds since the Unix epoch -// -// Any of the parameters can be NULL and they'll be skipped. -// -// The function will return 1 if the capture index is valid, or 0 if the index is invalid -// If the index is invalid, the values will be unchanged -// -// Note: when captures are deleted in the UI they will remain in this list, so the -// capture path may not exist anymore. -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename, - uint32_t *pathlength, uint64_t *timestamp); - -// Sets the comments associated with a capture file. These comments are displayed in the -// UI program when opening. -// -// filePath should be a path to the capture file to add comments to. If set to NULL or "" -// the most recent capture file created made will be used instead. -// comments should be a NULL-terminated UTF-8 string to add as comments. -// -// Any existing comments will be overwritten. -typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath, - const char *comments); - -// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)(); - -// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers. -// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for -// backwards compatibility with old code, it is castable either way since it's ABI compatible -// as the same function pointer type. -typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected; - -// This function will launch the Replay UI associated with the RenderDoc library injected -// into the running application. -// -// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter -// to connect to this application -// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open -// if cmdline is NULL, the command line will be empty. -// -// returns the PID of the replay UI if successful, 0 if not successful. -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl, - const char *cmdline); - -// RenderDoc can return a higher version than requested if it's backwards compatible, -// this function returns the actual version returned. If a parameter is NULL, it will be -// ignored and the others will be filled out. -typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch); - -////////////////////////////////////////////////////////////////////////// -// Capturing functions -// - -// A device pointer is a pointer to the API's root handle. -// -// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc -typedef void *RENDERDOC_DevicePointer; - -// A window handle is the OS's native window handle -// -// This would be an HWND, GLXDrawable, etc -typedef void *RENDERDOC_WindowHandle; - -// A helper macro for Vulkan, where the device handle cannot be used directly. -// -// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use. -// -// Specifically, the value needed is the dispatch table pointer, which sits as the first -// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and -// indirect once. -#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst))) - -// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will -// respond to keypresses. Neither parameter can be NULL -typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device, - RENDERDOC_WindowHandle wndHandle); - -// capture the next frame on whichever window and API is currently considered active -typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)(); - -// capture the next N frames on whichever window and API is currently considered active -typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames); - -// When choosing either a device pointer or a window handle to capture, you can pass NULL. -// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify -// any API rendering to a specific window, or a specific API instance rendering to any window, -// or in the simplest case of one window and one API, you can just pass NULL for both. -// -// In either case, if there are two or more possible matching (device,window) pairs it -// is undefined which one will be captured. -// -// Note: for headless rendering you can pass NULL for the window handle and either specify -// a device pointer or leave it NULL as above. - -// Immediately starts capturing API calls on the specified device pointer and window handle. -// -// If there is no matching thing to capture (e.g. no supported API has been initialised), -// this will do nothing. -// -// The results are undefined (including crashes) if two captures are started overlapping, -// even on separate devices and/oror windows. -typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device, - RENDERDOC_WindowHandle wndHandle); - -// Returns whether or not a frame capture is currently ongoing anywhere. -// -// This will return 1 if a capture is ongoing, and 0 if there is no capture running -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)(); - -// Ends capturing immediately. -// -// This will return 1 if the capture succeeded, and 0 if there was an error capturing. -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device, - RENDERDOC_WindowHandle wndHandle); - -// Ends capturing immediately and discard any data stored without saving to disk. -// -// This will return 1 if the capture was discarded, and 0 if there was an error or no capture -// was in progress -typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device, - RENDERDOC_WindowHandle wndHandle); - -////////////////////////////////////////////////////////////////////////////////////////////////// -// RenderDoc API versions -// - -// RenderDoc uses semantic versioning (http://semver.org/). -// -// MAJOR version is incremented when incompatible API changes happen. -// MINOR version is incremented when functionality is added in a backwards-compatible manner. -// PATCH version is incremented when backwards-compatible bug fixes happen. -// -// Note that this means the API returned can be higher than the one you might have requested. -// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned -// instead of 1.0.0. You can check this with the GetAPIVersion entry point -typedef enum RENDERDOC_Version { - eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00 - eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01 - eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02 - eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00 - eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01 - eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02 - eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00 - eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00 - eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00 - eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01 - eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02 -} RENDERDOC_Version; - -// API version changelog: -// -// 1.0.0 - initial release -// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered -// by keypress or TriggerCapture, instead of Start/EndFrameCapture. -// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation -// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new -// function pointer is added to the end of the struct, the original layout is identical -// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote -// replay/remote server concept in replay UI) -// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these -// are captures and not debug logging files. This is the first API version in the v1.0 -// branch. -// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be -// displayed in the UI program on load. -// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions -// which allows users to opt-in to allowing unsupported vendor extensions to function. -// Should be used at the user's own risk. -// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to -// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to -// 0xdddddddd of uninitialised buffer contents. -// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop -// capturing without saving anything to disk. -// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening -// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option. - -typedef struct RENDERDOC_API_1_4_1 -{ - pRENDERDOC_GetAPIVersion GetAPIVersion; - - pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32; - pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32; - - pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32; - pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32; - - pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys; - pRENDERDOC_SetCaptureKeys SetCaptureKeys; - - pRENDERDOC_GetOverlayBits GetOverlayBits; - pRENDERDOC_MaskOverlayBits MaskOverlayBits; - - // Shutdown was renamed to RemoveHooks in 1.4.1. - // These unions allow old code to continue compiling without changes - union - { - pRENDERDOC_Shutdown Shutdown; - pRENDERDOC_RemoveHooks RemoveHooks; - }; - pRENDERDOC_UnloadCrashHandler UnloadCrashHandler; - - // Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2. - // These unions allow old code to continue compiling without changes - union - { - // deprecated name - pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate; - // current name - pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate; - }; - union - { - // deprecated name - pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate; - // current name - pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate; - }; - - pRENDERDOC_GetNumCaptures GetNumCaptures; - pRENDERDOC_GetCapture GetCapture; - - pRENDERDOC_TriggerCapture TriggerCapture; - - // IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1. - // This union allows old code to continue compiling without changes - union - { - // deprecated name - pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected; - // current name - pRENDERDOC_IsTargetControlConnected IsTargetControlConnected; - }; - pRENDERDOC_LaunchReplayUI LaunchReplayUI; - - pRENDERDOC_SetActiveWindow SetActiveWindow; - - pRENDERDOC_StartFrameCapture StartFrameCapture; - pRENDERDOC_IsFrameCapturing IsFrameCapturing; - pRENDERDOC_EndFrameCapture EndFrameCapture; - - // new function in 1.1.0 - pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture; - - // new function in 1.2.0 - pRENDERDOC_SetCaptureFileComments SetCaptureFileComments; - - // new function in 1.4.0 - pRENDERDOC_DiscardFrameCapture DiscardFrameCapture; -} RENDERDOC_API_1_4_2; - -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_0_0; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_0_1; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_0_2; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_1_0; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_1_1; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_1_2; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_2_0; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_3_0; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_4_0; -typedef RENDERDOC_API_1_4_2 RENDERDOC_API_1_4_0; - -////////////////////////////////////////////////////////////////////////////////////////////////// -// RenderDoc API entry point -// -// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available. -// -// The name is the same as the typedef - "RENDERDOC_GetAPI" -// -// This function is not thread safe, and should not be called on multiple threads at once. -// Ideally, call this once as early as possible in your application's startup, before doing -// any API work, since some configuration functionality etc has to be done also before -// initialising any APIs. -// -// Parameters: -// version is a single value from the RENDERDOC_Version above. -// -// outAPIPointers will be filled out with a pointer to the corresponding struct of function -// pointers. -// -// Returns: -// 1 - if the outAPIPointers has been filled with a pointer to the API struct requested -// 0 - if the requested version is not supported or the arguments are invalid. -// -typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers); - -#ifdef __cplusplus -} // extern "C" -#endif diff --git a/src/lightmap/stacktrace.cpp b/src/lightmap/stacktrace.cpp deleted file mode 100644 index 37f4383..0000000 --- a/src/lightmap/stacktrace.cpp +++ /dev/null @@ -1,198 +0,0 @@ - -#include "stacktrace.h" - -#ifdef WIN32 -#include -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#ifdef WIN32 -#pragma comment(lib, "dbghelp.lib") -class NativeSymbolResolver -{ -public: - NativeSymbolResolver() - { - SymInitialize(GetCurrentProcess(), nullptr, TRUE); - GetModuleInformation(GetCurrentProcess(), GetModuleHandle(0), &moduleInfo, sizeof(MODULEINFO)); - } - - ~NativeSymbolResolver() - { - SymCleanup(GetCurrentProcess()); - } - - std::string GetName(void* frame) - { - std::string s; - - unsigned char buffer[sizeof(IMAGEHLP_SYMBOL64) + 128]; - IMAGEHLP_SYMBOL64* symbol64 = reinterpret_cast(buffer); - memset(symbol64, 0, sizeof(IMAGEHLP_SYMBOL64) + 128); - symbol64->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); - symbol64->MaxNameLength = 128; - - DWORD64 displacement = 0; - BOOL result = SymGetSymFromAddr64(GetCurrentProcess(), (DWORD64)frame, &displacement, symbol64); - if (result) - { - if ((DWORD64)frame < (DWORD64)moduleInfo.lpBaseOfDll || (DWORD64)frame >= ((DWORD64)moduleInfo.lpBaseOfDll + moduleInfo.SizeOfImage)) - return s; // Ignore anything not from the exe itself - - IMAGEHLP_LINE64 line64; - DWORD displacement = 0; - memset(&line64, 0, sizeof(IMAGEHLP_LINE64)); - line64.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - result = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)frame, &displacement, &line64); - if (result) - { - s = std::string("Called from ") + symbol64->Name + " at " + line64.FileName + ", line " + std::to_string(line64.LineNumber) + "\n", symbol64->Name; - } - else - { - s = std::string("Called from ") + symbol64->Name + "\n"; - } - } - - return s; - } - - MODULEINFO moduleInfo = {}; -}; -#else -class NativeSymbolResolver -{ -public: - std::string GetName(void* frame) - { - std::string s; - char** strings; - void* frames[1] = { frame }; - strings = backtrace_symbols(frames, 1); - - // Decode the strings - char* ptr = strings[0]; - char* filename = ptr; - const char* function = ""; - - // Find function name - while (*ptr) - { - if (*ptr == '(') // Found function name - { - *(ptr++) = 0; - function = ptr; - break; - } - ptr++; - } - - // Find offset - if (function[0]) // Only if function was found - { - while (*ptr) - { - if (*ptr == '+') // Found function offset - { - *(ptr++) = 0; - break; - } - if (*ptr == ')') // Not found function offset, but found, end of function - { - *(ptr++) = 0; - break; - } - ptr++; - } - } - - int status; - char* new_function = abi::__cxa_demangle(function, nullptr, nullptr, &status); - if (new_function) // Was correctly decoded - { - function = new_function; - } - - s = std::string("Called from ") + function + " at " + filename + "\n"; - - if (new_function) - { - free(new_function); - } - - free(strings); - return s; - } -}; -#endif - -static int CaptureStackTrace(int max_frames, void** out_frames) -{ - memset(out_frames, 0, sizeof(void*) * max_frames); - -#ifdef _WIN64 - // RtlCaptureStackBackTrace doesn't support RtlAddFunctionTable.. - - CONTEXT context; - RtlCaptureContext(&context); - - UNWIND_HISTORY_TABLE history; - memset(&history, 0, sizeof(UNWIND_HISTORY_TABLE)); - - ULONG64 establisherframe = 0; - PVOID handlerdata = nullptr; - - int frame; - for (frame = 0; frame < max_frames; frame++) - { - ULONG64 imagebase; - PRUNTIME_FUNCTION rtfunc = RtlLookupFunctionEntry(context.Rip, &imagebase, &history); - - KNONVOLATILE_CONTEXT_POINTERS nvcontext; - memset(&nvcontext, 0, sizeof(KNONVOLATILE_CONTEXT_POINTERS)); - if (!rtfunc) - { - // Leaf function - context.Rip = (ULONG64)(*(PULONG64)context.Rsp); - context.Rsp += 8; - } - else - { - RtlVirtualUnwind(UNW_FLAG_NHANDLER, imagebase, context.Rip, rtfunc, &context, &handlerdata, &establisherframe, &nvcontext); - } - - if (!context.Rip) - break; - - out_frames[frame] = (void*)context.Rip; - } - return frame; - -#elif defined(WIN32) - return 0;//return RtlCaptureStackBackTrace(0, MIN(max_frames, 32), out_frames, nullptr); -#else - return backtrace(out_frames, max_frames); -#endif -} - -std::string CaptureStackTraceText(int framesToSkip) -{ - void* frames[32]; - int numframes = CaptureStackTrace(32, frames); - - NativeSymbolResolver nativeSymbols; - - std::string s; - for (int i = framesToSkip + 1; i < numframes; i++) - { - s += nativeSymbols.GetName(frames[i]); - } - return s; -} diff --git a/src/lightmap/stacktrace.h b/src/lightmap/stacktrace.h deleted file mode 100644 index 7bc8409..0000000 --- a/src/lightmap/stacktrace.h +++ /dev/null @@ -1,6 +0,0 @@ - -#pragma once - -#include - -std::string CaptureStackTraceText(int framesToSkip); diff --git a/src/lightmap/surfaceclip.cpp b/src/lightmap/surfaceclip.cpp deleted file mode 100644 index a0aa1ca..0000000 --- a/src/lightmap/surfaceclip.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "surfaceclip.h" - -inline bool PointOnSide(const vec2& p, const vec2& v1, const vec2& v2, float tolerance) -{ - vec2 p2 = p + normalize(vec2(-(v2.y - v1.y), v2.x - v1.x)) * tolerance; - return (p2.y - v1.y) * (v2.x - v1.x) + (v1.x - p2.x) * (v2.y - v1.y) >= 0; -} - - -inline bool PointBeyondSide(const vec2& p, const vec2& v1, const vec2& v2) -{ - vec2 p2 = p - normalize(vec2(-(v2.y - v1.y), v2.x - v1.x)); // What a hack! - return (p2.y - v1.y) * (v2.x - v1.x) + (v1.x - p2.x) * (v2.y - v1.y) < 0; -} - -SurfaceClip::SurfaceClip(Surface* surface) -{ - sampleWidth = float(surface->texWidth); - sampleHeight = float(surface->texHeight); - - // Transformation matrix - mat3 base; - base[0] = surface->worldStepX.x; - base[1] = surface->worldStepX.y; - base[2] = surface->worldStepX.z; - base[3] = surface->worldStepY.x; - base[4] = surface->worldStepY.y; - base[5] = surface->worldStepY.z; - base[6] = surface->plane.a; - base[7] = surface->plane.b; - base[8] = surface->plane.c; - - mat3 inverseProjection = mat3::inverse(base); - - // Transform vertices to XY and triangulate - vertices.reserve(surface->verts.size()); - - for (const auto& vertex : surface->verts) - { - auto flattenedVertex = inverseProjection * vertex; - - vertices.emplace_back(flattenedVertex.x, flattenedVertex.y); - - if (vertices.empty()) - { - bounds = BBox(flattenedVertex, flattenedVertex); - } - else - { - bounds.AddPoint(flattenedVertex); - } - } - - // Walls have "Z" like pattern for vertices - if (surface->type != ST_CEILING && surface->type != ST_FLOOR) - { - if (vertices.size() == 4) - { - std::swap(vertices[vertices.size() - 2], vertices[vertices.size() - 1]); - } - } - - auto isConvex = [&]() { - for (size_t i = 2; i < vertices.size(); ++i) - { - if (!PointBeyondSide(vertices[i - 1], vertices[i - 2], vertices[i])) - { - return false; - } - } - return PointBeyondSide(vertices[vertices.size() - 1], vertices[vertices.size() - 2], vertices[0]) && PointBeyondSide(vertices[0], vertices[vertices.size() - 1], vertices[1]); - }; - - // Fix vertex order - if (!isConvex()) - { - for (size_t i = 0; i < vertices.size() / 2; ++i) - { - std::swap(vertices[i], vertices[vertices.size() - 1 - i]); - } - } - - // Init misc. variables - boundsWidth = bounds.max.x - bounds.min.x; - boundsHeight = bounds.max.y - bounds.min.y; - - offsetW = boundsWidth / sampleWidth; - offsetH = boundsHeight / sampleHeight; - - tolerance = (offsetH > offsetW ? offsetH : offsetW) * 2.0f; -} - -bool SurfaceClip::PointInBounds(const vec2& p, float tolerance) const -{ - for (size_t i = 1; i < vertices.size(); ++i) - { - if (!PointOnSide(p, vertices[i - 1], vertices[i], tolerance)) - { - return false; - } - } - return PointOnSide(p, vertices[vertices.size() - 1], vertices[0], tolerance); -} - -bool SurfaceClip::SampleIsInBounds(float x, float y) const -{ - return PointInBounds(vec2((x / float(sampleWidth)) * boundsWidth + bounds.min.x + offsetW, (y / float(sampleHeight)) * boundsHeight + bounds.min.y + offsetH), tolerance); -} \ No newline at end of file diff --git a/src/lightmap/surfaceclip.h b/src/lightmap/surfaceclip.h deleted file mode 100644 index 3f439e0..0000000 --- a/src/lightmap/surfaceclip.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "lightmap/levelmesh.h" -#include "math/mathlib.h" -#include "delauneytriangulator.h" - -class SurfaceClip -{ - std::vector vertices; - - float sampleWidth; - float sampleHeight; - - BBox bounds; - float boundsWidth; - float boundsHeight; - float offsetW; - float offsetH; - float tolerance; - - // Local space - bool PointInBounds(const vec2& p, float tolerance) const; -public: - SurfaceClip(Surface* surface); - - // Task XY space. Tolerates points close enough to the surface to avoid missing used samples - bool SampleIsInBounds(float x, float y) const; -}; \ No newline at end of file