mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-24 21:01:15 +00:00
Remove original lightmapper implementation
This commit is contained in:
parent
f689ea3640
commit
2c786e0718
21 changed files with 0 additions and 6636 deletions
|
@ -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(), ¢roids[0], &work_buffer[0]);
|
||||
}
|
||||
|
||||
float TriangleMeshShape::sweep(TriangleMeshShape *shape1, SphereShape *shape2, const vec3 &target)
|
||||
{
|
||||
return sweep(shape1, shape2, shape1->root, target);
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2)
|
||||
{
|
||||
return find_any_hit(shape1, shape2, shape1->root, shape2->root);
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2)
|
||||
{
|
||||
return find_any_hit(shape1, shape2, shape1->root);
|
||||
}
|
||||
|
||||
std::vector<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]]
|
||||
};
|
||||
|
||||
// Moeller–Trumbore ray-triangle intersection algorithm:
|
||||
|
||||
vec3 D = ray.end - ray.start;
|
||||
|
||||
// Find vectors for two edges sharing p[0]
|
||||
vec3 e1 = p[1] - p[0];
|
||||
vec3 e2 = p[2] - p[0];
|
||||
|
||||
// Begin calculating determinant - also used to calculate u parameter
|
||||
vec3 P = cross(D, e2);
|
||||
float det = dot(e1, P);
|
||||
|
||||
// Backface check
|
||||
//if (det < 0.0f)
|
||||
// return 1.0f;
|
||||
|
||||
// If determinant is near zero, ray lies in plane of triangle
|
||||
if (det > -FLT_EPSILON && det < FLT_EPSILON)
|
||||
return 1.0f;
|
||||
|
||||
float inv_det = 1.0f / det;
|
||||
|
||||
// Calculate distance from p[0] to ray origin
|
||||
vec3 T = ray.start - p[0];
|
||||
|
||||
// Calculate u parameter and test bound
|
||||
float u = dot(T, P) * inv_det;
|
||||
|
||||
// Check if the intersection lies outside of the triangle
|
||||
if (u < 0.f || u > 1.f)
|
||||
return 1.0f;
|
||||
|
||||
// Prepare to test v parameter
|
||||
vec3 Q = cross(T, e1);
|
||||
|
||||
// Calculate V parameter and test bound
|
||||
float v = dot(D, Q) * inv_det;
|
||||
|
||||
// The intersection lies outside of the triangle
|
||||
if (v < 0.f || u + v > 1.f)
|
||||
return 1.0f;
|
||||
|
||||
float t = dot(e2, Q) * inv_det;
|
||||
if (t <= FLT_EPSILON)
|
||||
return 1.0f;
|
||||
|
||||
// Return hit location on triangle in barycentric coordinates
|
||||
barycentricB = u;
|
||||
barycentricC = v;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target)
|
||||
{
|
||||
// Convert to ray test by expanding the AABB:
|
||||
|
||||
CollisionBBox aabb = shape1->nodes[a].aabb;
|
||||
aabb.Extents += shape2->radius;
|
||||
|
||||
return IntersectionTest::ray_aabb(RayBBox(shape2->center, target), aabb) == IntersectionTest::overlap;
|
||||
}
|
||||
|
||||
float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const vec3 &target)
|
||||
{
|
||||
const int start_element = shape1->nodes[a].element_index;
|
||||
|
||||
vec3 p[3] =
|
||||
{
|
||||
shape1->vertices[shape1->elements[start_element]],
|
||||
shape1->vertices[shape1->elements[start_element + 1]],
|
||||
shape1->vertices[shape1->elements[start_element + 2]]
|
||||
};
|
||||
|
||||
vec3 c = shape2->center;
|
||||
vec3 e = target;
|
||||
float r = shape2->radius;
|
||||
|
||||
// Dynamic intersection test between a ray and the minkowski sum of the sphere and polygon:
|
||||
|
||||
vec3 n = normalize(cross(p[1] - p[0], p[2] - p[0]));
|
||||
vec4 plane(n, -dot(n, p[0]));
|
||||
|
||||
// Step 1: Plane intersect test
|
||||
|
||||
float sc = dot(plane, vec4(c, 1.0f));
|
||||
float se = dot(plane, vec4(e, 1.0f));
|
||||
bool same_side = sc * se > 0.0f;
|
||||
|
||||
if (same_side && std::abs(sc) > r && std::abs(se) > r)
|
||||
return 1.0f;
|
||||
|
||||
// Step 1a: Check if point is in polygon (using crossing ray test in 2d)
|
||||
{
|
||||
float t = (sc - r) / (sc - se);
|
||||
|
||||
vec3 vt = c + (e - c) * t;
|
||||
|
||||
vec3 u0 = p[1] - p[0];
|
||||
vec3 u1 = p[2] - p[0];
|
||||
|
||||
vec2 v_2d[3] =
|
||||
{
|
||||
vec2(0.0f, 0.0f),
|
||||
vec2(dot(u0, u0), 0.0f),
|
||||
vec2(0.0f, dot(u1, u1))
|
||||
};
|
||||
|
||||
vec2 point(dot(u0, vt), dot(u1, vt));
|
||||
|
||||
bool inside = false;
|
||||
vec2 e0 = v_2d[2];
|
||||
bool y0 = e0.y >= point.y;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
vec2 e1 = v_2d[i];
|
||||
bool y1 = e1.y >= point.y;
|
||||
|
||||
if (y0 != y1 && ((e1.y - point.y) * (e0.x - e1.x) >= (e1.x - point.x) * (e0.y - e1.y)) == y1)
|
||||
inside = !inside;
|
||||
|
||||
y0 = y1;
|
||||
e0 = e1;
|
||||
}
|
||||
|
||||
if (inside)
|
||||
return t;
|
||||
}
|
||||
|
||||
// Step 2: Edge intersect test
|
||||
|
||||
vec3 ke[3] =
|
||||
{
|
||||
p[1] - p[0],
|
||||
p[2] - p[1],
|
||||
p[0] - p[2],
|
||||
};
|
||||
|
||||
vec3 kg[3] =
|
||||
{
|
||||
p[0] - c,
|
||||
p[1] - c,
|
||||
p[2] - c,
|
||||
};
|
||||
|
||||
vec3 ks = e - c;
|
||||
|
||||
float kgg[3];
|
||||
float kgs[3];
|
||||
float kss[3];
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
float kee = dot(ke[i], ke[i]);
|
||||
float keg = dot(ke[i], kg[i]);
|
||||
float kes = dot(ke[i], ks);
|
||||
kgg[i] = dot(kg[i], kg[i]);
|
||||
kgs[i] = dot(kg[i], ks);
|
||||
kss[i] = dot(ks, ks);
|
||||
|
||||
float aa = kee * kss[i] - kes * kes;
|
||||
float bb = 2 * (keg * kes - kee * kgs[i]);
|
||||
float cc = kee * (kgg[i] - r * r) - keg * keg;
|
||||
|
||||
float sign = (bb >= 0.0f) ? 1.0f : -1.0f;
|
||||
float q = -0.5f * (bb + sign * std::sqrt(bb * bb - 4 * aa * cc));
|
||||
float t0 = q / aa;
|
||||
float t1 = cc / q;
|
||||
|
||||
float t;
|
||||
if (t0 < 0.0f || t0 > 1.0f)
|
||||
t = t1;
|
||||
else if (t1 < 0.0f || t1 > 1.0f)
|
||||
t = t0;
|
||||
else
|
||||
t = std::min(t0, t1);
|
||||
|
||||
if (t >= 0.0f && t <= 1.0f)
|
||||
{
|
||||
vec3 ct = c + ks * t;
|
||||
float d = dot(ct - p[i], ke[i]);
|
||||
if (d >= 0.0f && d <= kee)
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Point intersect test
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
float aa = kss[i];
|
||||
float bb = -2.0f * kgs[i];
|
||||
float cc = kgg[i] - r * r;
|
||||
|
||||
float sign = (bb >= 0.0f) ? 1.0f : -1.0f;
|
||||
float q = -0.5f * (bb + sign * std::sqrt(bb * bb - 4 * aa * cc));
|
||||
float t0 = q / aa;
|
||||
float t1 = cc / q;
|
||||
|
||||
float t;
|
||||
if (t0 < 0.0f || t0 > 1.0f)
|
||||
t = t1;
|
||||
else if (t1 < 0.0f || t1 > 1.0f)
|
||||
t = t0;
|
||||
else
|
||||
t = std::min(t0, t1);
|
||||
|
||||
if (t >= 0.0f && t <= 1.0f)
|
||||
return t;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::overlap_bv(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b)
|
||||
{
|
||||
return IntersectionTest::aabb(shape1->nodes[a].aabb, shape2->nodes[b].aabb) == IntersectionTest::overlap;
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::overlap_bv_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a)
|
||||
{
|
||||
return IntersectionTest::sphere_aabb(shape2->center, shape2->radius, shape1->nodes[a].aabb) == IntersectionTest::overlap;
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::overlap_triangle_triangle(TriangleMeshShape *shape1, TriangleMeshShape *shape2, int a, int b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::overlap_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int shape1_node_index)
|
||||
{
|
||||
// http://realtimecollisiondetection.net/blog/?p=103
|
||||
|
||||
int element_index = shape1->nodes[shape1_node_index].element_index;
|
||||
|
||||
vec3 P = shape2->center;
|
||||
vec3 A = shape1->vertices[shape1->elements[element_index]] - P;
|
||||
vec3 B = shape1->vertices[shape1->elements[element_index + 1]] - P;
|
||||
vec3 C = shape1->vertices[shape1->elements[element_index + 2]] - P;
|
||||
float r = shape2->radius;
|
||||
float rr = r * r;
|
||||
|
||||
// Testing if sphere lies outside the triangle plane
|
||||
vec3 V = cross(B - A, C - A);
|
||||
float d = dot(A, V);
|
||||
float e = dot(V, V);
|
||||
bool sep1 = d * d > rr * e;
|
||||
|
||||
// Testing if sphere lies outside a triangle vertex
|
||||
float aa = dot(A, A);
|
||||
float ab = dot(A, B);
|
||||
float ac = dot(A, C);
|
||||
float bb = dot(B, B);
|
||||
float bc = dot(B, C);
|
||||
float cc = dot(C, C);
|
||||
bool sep2 = (aa > rr) && (ab > aa) && (ac > aa);
|
||||
bool sep3 = (bb > rr) && (ab > bb) && (bc > bb);
|
||||
bool sep4 = (cc > rr) && (ac > cc) && (bc > cc);
|
||||
|
||||
// Testing if sphere lies outside a triangle edge
|
||||
vec3 AB = B - A;
|
||||
vec3 BC = C - B;
|
||||
vec3 CA = A - C;
|
||||
float d1 = ab - aa;
|
||||
float d2 = bc - bb;
|
||||
float d3 = ac - cc;
|
||||
float e1 = dot(AB, AB);
|
||||
float e2 = dot(BC, BC);
|
||||
float e3 = dot(CA, CA);
|
||||
vec3 Q1 = A * e1 - AB * d1;
|
||||
vec3 Q2 = B * e2 - BC * d2;
|
||||
vec3 Q3 = C * e3 - CA * d3;
|
||||
vec3 QC = C * e1 - Q1;
|
||||
vec3 QA = A * e2 - Q2;
|
||||
vec3 QB = B * e3 - Q3;
|
||||
bool sep5 = (dot(Q1, Q1) > rr * e1 * e1) && (dot(Q1, QC) > 0.0f);
|
||||
bool sep6 = (dot(Q2, Q2) > rr * e2 * e2) && (dot(Q2, QA) > 0.0f);
|
||||
bool sep7 = (dot(Q3, Q3) > rr * e3 * e3) && (dot(Q3, QB) > 0.0f);
|
||||
|
||||
bool separated = sep1 || sep2 || sep3 || sep4 || sep5 || sep6 || sep7;
|
||||
return (!separated);
|
||||
}
|
||||
|
||||
bool TriangleMeshShape::is_leaf(int node_index)
|
||||
{
|
||||
return nodes[node_index].element_index != -1;
|
||||
}
|
||||
|
||||
float TriangleMeshShape::volume(int node_index)
|
||||
{
|
||||
const vec3 &extents = nodes[node_index].aabb.Extents;
|
||||
return extents.x * extents.y * extents.z;
|
||||
}
|
||||
|
||||
int TriangleMeshShape::get_min_depth() const
|
||||
{
|
||||
std::function<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 ¢er1, float radius1, const vec3 ¢er2, float radius2)
|
||||
{
|
||||
vec3 h = center1 - center2;
|
||||
float square_distance = dot(h, h);
|
||||
float radius_sum = radius1 + radius2;
|
||||
if (square_distance > radius_sum * radius_sum)
|
||||
return disjoint;
|
||||
else
|
||||
return overlap;
|
||||
}
|
||||
|
||||
IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const vec3 ¢er, float radius, const BBox &aabb)
|
||||
{
|
||||
vec3 a = aabb.min - center;
|
||||
vec3 b = center - aabb.max;
|
||||
a.x = std::max(a.x, 0.0f);
|
||||
a.y = std::max(a.y, 0.0f);
|
||||
a.z = std::max(a.z, 0.0f);
|
||||
b.x = std::max(b.x, 0.0f);
|
||||
b.y = std::max(b.y, 0.0f);
|
||||
b.z = std::max(b.z, 0.0f);
|
||||
vec3 e = a + b;
|
||||
float d = dot(e, e);
|
||||
if (d > radius * radius)
|
||||
return disjoint;
|
||||
else
|
||||
return overlap;
|
||||
}
|
||||
|
||||
IntersectionTest::OverlapResult IntersectionTest::aabb(const BBox &a, const BBox &b)
|
||||
{
|
||||
if (a.min.x > b.max.x || b.min.x > a.max.x ||
|
||||
a.min.y > b.max.y || b.min.y > a.max.y ||
|
||||
a.min.z > b.max.z || b.min.z > a.max.z)
|
||||
{
|
||||
return disjoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
return overlap;
|
||||
}
|
||||
}
|
||||
|
||||
IntersectionTest::Result IntersectionTest::frustum_aabb(const FrustumPlanes &frustum, const BBox &box)
|
||||
{
|
||||
bool is_intersecting = false;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
Result result = plane_aabb(frustum.planes[i], box);
|
||||
if (result == outside)
|
||||
return outside;
|
||||
else if (result == intersecting)
|
||||
is_intersecting = true;
|
||||
break;
|
||||
}
|
||||
if (is_intersecting)
|
||||
return intersecting;
|
||||
else
|
||||
return inside;
|
||||
}
|
||||
|
||||
IntersectionTest::Result IntersectionTest::frustum_obb(const FrustumPlanes &frustum, const OrientedBBox &box)
|
||||
{
|
||||
bool is_intersecting = false;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
Result result = plane_obb(frustum.planes[i], box);
|
||||
if (result == outside)
|
||||
return outside;
|
||||
else if (result == intersecting)
|
||||
is_intersecting = true;
|
||||
}
|
||||
if (is_intersecting)
|
||||
return intersecting;
|
||||
else
|
||||
return inside;
|
||||
}
|
||||
|
||||
static const uint32_t clearsignbitmask[] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff };
|
||||
|
||||
IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const RayBBox &ray, const CollisionBBox &aabb)
|
||||
{
|
||||
#ifndef NO_SSE
|
||||
|
||||
__m128 v = _mm_loadu_ps(&ray.v.x);
|
||||
__m128 w = _mm_loadu_ps(&ray.w.x);
|
||||
__m128 h = _mm_loadu_ps(&aabb.Extents.x);
|
||||
__m128 c = _mm_sub_ps(_mm_loadu_ps(&ray.c.x), _mm_loadu_ps(&aabb.Center.x));
|
||||
|
||||
__m128 clearsignbit = _mm_loadu_ps(reinterpret_cast<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;
|
||||
}
|
|
@ -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 ¢er, float radius) : center(center), radius(radius) { }
|
||||
|
||||
vec3 center;
|
||||
float radius = 0.0f;
|
||||
};
|
||||
|
||||
struct TraceHit
|
||||
{
|
||||
float fraction = 1.0f;
|
||||
int triangle = -1;
|
||||
float b = 0.0f;
|
||||
float c = 0.0f;
|
||||
};
|
||||
|
||||
class CollisionBBox : public BBox
|
||||
{
|
||||
public:
|
||||
CollisionBBox() = default;
|
||||
|
||||
CollisionBBox(const vec3 &aabb_min, const vec3 &aabb_max) : BBox(aabb_min, aabb_max)
|
||||
{
|
||||
auto halfmin = aabb_min * 0.5f;
|
||||
auto halfmax = aabb_max * 0.5f;
|
||||
Center = halfmax + halfmin;
|
||||
Extents = halfmax - halfmin;
|
||||
}
|
||||
|
||||
vec3 Center;
|
||||
vec3 Extents;
|
||||
float ssePadding = 0.0f; // Needed to safely load Extents directly into a sse register
|
||||
};
|
||||
|
||||
class RayBBox
|
||||
{
|
||||
public:
|
||||
RayBBox(const vec3 &ray_start, const vec3 &ray_end) : start(ray_start), end(ray_end)
|
||||
{
|
||||
c = (ray_start + ray_end) * 0.5f;
|
||||
w = ray_end - c;
|
||||
v.x = std::abs(w.x);
|
||||
v.y = std::abs(w.y);
|
||||
v.z = std::abs(w.z);
|
||||
}
|
||||
|
||||
vec3 start, end;
|
||||
vec3 c, w, v;
|
||||
float ssePadding = 0.0f; // Needed to safely load v directly into a sse register
|
||||
};
|
||||
|
||||
class TriangleMeshShape
|
||||
{
|
||||
public:
|
||||
TriangleMeshShape(const vec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements);
|
||||
|
||||
int get_min_depth() const;
|
||||
int get_max_depth() const;
|
||||
float get_average_depth() const;
|
||||
float get_balanced_depth() const;
|
||||
|
||||
const CollisionBBox &get_bbox() const { return nodes[root].aabb; }
|
||||
|
||||
static float sweep(TriangleMeshShape *shape1, SphereShape *shape2, const vec3 &target);
|
||||
|
||||
static bool find_any_hit(TriangleMeshShape *shape1, TriangleMeshShape *shape2);
|
||||
static bool find_any_hit(TriangleMeshShape *shape1, SphereShape *shape2);
|
||||
static bool find_any_hit(TriangleMeshShape *shape, const vec3 &ray_start, const vec3 &ray_end);
|
||||
|
||||
static std::vector<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 ¢er1, float radius1, const vec3 ¢er2, float radius2);
|
||||
static OverlapResult sphere_aabb(const vec3 ¢er, float radius, const BBox &aabb);
|
||||
static OverlapResult aabb(const BBox &a, const BBox &b);
|
||||
static Result frustum_aabb(const FrustumPlanes &frustum, const BBox &box);
|
||||
static Result frustum_obb(const FrustumPlanes &frustum, const OrientedBBox &box);
|
||||
static OverlapResult ray_aabb(const RayBBox &ray, const CollisionBBox &box);
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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";
|
|
@ -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";
|
|
@ -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
|
@ -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
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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 = ℑ
|
||||
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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string CaptureStackTraceText(int framesToSkip);
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
Loading…
Reference in a new issue