Remove original lightmapper implementation

This commit is contained in:
Magnus Norddahl 2024-01-16 12:47:24 +01:00
parent f689ea3640
commit 2c786e0718
21 changed files with 0 additions and 6636 deletions

View file

@ -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 <algorithm>
#include <functional>
#include <cfloat>
#ifndef NO_SSE
#include <immintrin.h>
#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<int> triangles;
std::vector<vec3> 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<int> work_buffer(num_triangles * 2);
root = subdivide(&triangles[0], (int)triangles.size(), &centroids[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<int> TriangleMeshShape::find_all_hits(TriangleMeshShape* shape1, SphereShape* shape2)
{
std::vector<int> 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<int>& 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]]
};
// MoellerTrumbore 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<int(int, int)> 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<int(int, int)> 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<float(int, int)> 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 &center1, float radius1, const vec3 &center2, 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 &center, 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<const float*>(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;
}

View file

@ -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 <vector>
#include <cmath>
class SphereShape
{
public:
SphereShape() { }
SphereShape(const vec3 &center, 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<int> 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<Node>& 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<Node> 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<int>& 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 &center1, float radius1, const vec3 &center2, float radius2);
static OverlapResult sphere_aabb(const vec3 &center, 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);
};

View file

@ -1,250 +0,0 @@
#include "delauneytriangulator.h"
void DelauneyTriangulator::triangulate()
{
std::vector<Vertex*> 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::Vertex*> DelauneyTriangulator::create_ordered_vertex_list()
{
if (vertices.empty())
return {};
std::vector<Vertex*> 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::Vertex*> DelauneyTriangulator::remove_duplicates(const std::vector<Vertex*>& ordered_vertices)
{
// Link duplicates and remove them from the ordered list:
std::vector<Vertex*> 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<Vertex*>& 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::Triangle> DelauneyTriangulator::perform_delauney_triangulation(const std::vector<Vertex*>& vertices, const Triangle& super_triangle)
{
std::vector<Triangle> triangles;
// add supertriangle vertices to the end of the vertex list
triangles.push_back(super_triangle);
std::vector<Triangle_Edge> 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<Triangle>::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;
}

View file

@ -1,45 +0,0 @@
#pragma once
#include <vector>
#include <algorithm>
// 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<Vertex*, Vertex*> Triangle_Edge;
std::vector<Vertex> vertices;
std::vector<Triangle> triangles;
void triangulate();
private:
std::vector<Vertex*> create_ordered_vertex_list();
static std::vector<Vertex*> remove_duplicates(const std::vector<Vertex*>& ordered_vertices);
static void calculate_supertriangle(std::vector<Vertex*>& vertices, Triangle& super_triangle);
static void calc_cirumcenter(Triangle& triangle);
static std::vector<Triangle> perform_delauney_triangulation(const std::vector<Vertex*>& vertices, const Triangle& super_triangle);
};

View file

@ -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";

View file

@ -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";

View file

@ -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";

File diff suppressed because it is too large Load diff

View file

@ -1,228 +0,0 @@
#pragma once
#include <zvulkan/vulkandevice.h>
#include <zvulkan/vulkanobjects.h>
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<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
} raytrace;
struct
{
std::unique_ptr<VulkanImage> Image;
std::unique_ptr<VulkanImageView> View;
std::unique_ptr<VulkanFramebuffer> Framebuffer;
} resolve;
std::unique_ptr<VulkanBuffer> 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<SurfaceInfo> CreateSurfaceInfo();
std::vector<PortalInfo> CreatePortalInfo();
std::vector<CollisionNode> 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<VulkanDevice> device;
bool useRayQuery = true;
static const int SceneVertexBufferSize = 1 * 1024 * 1024;
std::unique_ptr<VulkanBuffer> sceneVertexBuffer;
SceneVertex* sceneVertices = nullptr;
int sceneVertexPos = 0;
static const int SceneLightBufferSize = 2 * 1024 * 1024;
std::unique_ptr<VulkanBuffer> sceneLightBuffer;
LightInfo* sceneLights = nullptr;
int sceneLightPos = 0;
std::unique_ptr<VulkanBuffer> vertexBuffer;
std::unique_ptr<VulkanBuffer> indexBuffer;
std::unique_ptr<VulkanBuffer> transferBuffer;
std::unique_ptr<VulkanBuffer> surfaceIndexBuffer;
std::unique_ptr<VulkanBuffer> surfaceBuffer;
std::unique_ptr<VulkanBuffer> portalBuffer;
std::unique_ptr<VulkanBuffer> nodesBuffer;
std::unique_ptr<VulkanBuffer> blScratchBuffer;
std::unique_ptr<VulkanBuffer> blAccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> blAccelStruct;
std::unique_ptr<VulkanBuffer> tlTransferBuffer;
std::unique_ptr<VulkanBuffer> tlScratchBuffer;
std::unique_ptr<VulkanBuffer> tlInstanceBuffer;
std::unique_ptr<VulkanBuffer> tlAccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> tlAccelStruct;
std::unique_ptr<VulkanShader> vertShader;
std::unique_ptr<VulkanShader> fragShader;
std::unique_ptr<VulkanShader> fragResolveShader;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout0;
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout1;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPassBegin;
std::unique_ptr<VulkanRenderPass> renderPassContinue;
std::unique_ptr<VulkanDescriptorPool> descriptorPool0;
std::unique_ptr<VulkanDescriptorPool> descriptorPool1;
std::unique_ptr<VulkanDescriptorSet> descriptorSet0;
std::unique_ptr<VulkanDescriptorSet> descriptorSet1;
} raytrace;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
std::unique_ptr<VulkanPipeline> pipeline;
std::unique_ptr<VulkanRenderPass> renderPass;
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
std::vector<std::unique_ptr<VulkanDescriptorSet>> descriptorSets;
std::unique_ptr<VulkanSampler> sampler;
} resolve;
std::unique_ptr<VulkanBuffer> uniformBuffer;
std::unique_ptr<VulkanBuffer> uniformTransferBuffer;
std::unique_ptr<VulkanFence> submitFence;
std::unique_ptr<VulkanCommandPool> cmdpool;
std::unique_ptr<VulkanCommandBuffer> cmdbuffer;
std::vector<LightmapImage> atlasImages;
static const int atlasImageSize = 2048;
};

File diff suppressed because it is too large Load diff

View file

@ -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 <vector>
#include <memory>
#include <string>
#include <cstring>
#include <map>
#include <set>
#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<int> 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<vec3> verts;
Plane plane;
BBox bounds;
// Surface material
std::string material;
std::vector<vec2> 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<ThingLight*> 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<vec3> texPixels;
inline uint32_t Area() const { return uint32_t(texWidth * texHeight); }
// UV coordinates for the vertices
std::vector<vec2> 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<Surface*> 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<std::unique_ptr<Surface>> surfaces;
std::vector<std::unique_ptr<LightmapTexture>> textures;
std::vector<SmoothingGroup> smoothingGroups;
std::vector<std::unique_ptr<Portal>> portals;
int defaultSamples = 16;
int textureWidth = 128;
int textureHeight = 128;
TArray<vec3> MeshVertices;
TArray<int> MeshUVIndex;
TArray<unsigned int> MeshElements;
TArray<int> MeshSurfaces;
std::unique_ptr<TriangleMeshShape> Collision;
private:
// Portal to portals[] index
std::map<Portal, int, IdenticalPortalComparator> portalCache;
// Portal lights
std::vector<std::unique_ptr<ThingLight>> portalLights;
std::set<Portal, RecursivePortalComparator> 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;
};

View file

@ -1,13 +0,0 @@
#include "lightmaptexture.h"
#include "framework/halffloat.h"
#include <algorithm>
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
}

View file

@ -1,20 +0,0 @@
#pragma once
#include <cstdint>
#include <vector>
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<uint16_t> mPixels;
};

View file

@ -1,218 +0,0 @@
#include "math/mathlib.h"
#include "pngwriter.h"
#include "framework/binfile.h"
#include "framework/templates.h"
#include "framework/halffloat.h"
#include <map>
#include <vector>
#include <algorithm>
#include <miniz/miniz.h>
#include <stdexcept>
#include <memory>
#include <cmath>
#include <string.h>
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 = &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<unsigned char> scanline_orig;
std::vector<unsigned char> 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<DataBuffer>((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<DataBuffer>(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;
}

View file

@ -1,78 +0,0 @@
#pragma once
#include <string>
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<const unsigned char*>(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);
};

View file

@ -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;
}
};

View file

@ -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 <stdint.h>
#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

View file

@ -1,198 +0,0 @@
#include "stacktrace.h"
#ifdef WIN32
#include <Windows.h>
#include <DbgHelp.h>
#include <psapi.h>
#else
#include <execinfo.h>
#include <cxxabi.h>
#include <cstring>
#include <cstdlib>
#include <memory>
#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<IMAGEHLP_SYMBOL64*>(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;
}

View file

@ -1,6 +0,0 @@
#pragma once
#include <string>
std::string CaptureStackTraceText(int framesToSkip);

View file

@ -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);
}

View file

@ -1,28 +0,0 @@
#pragma once
#include "lightmap/levelmesh.h"
#include "math/mathlib.h"
#include "delauneytriangulator.h"
class SurfaceClip
{
std::vector<vec2> 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;
};