virtualx-engine/thirdparty/meshoptimizer/overdrawanalyzer.cpp
reduz 77a045e902 Rework Mesh handling on scene importing.
-Reworked how meshes are treated by importer by using EditorSceneImporterMesh and EditorSceneImporterMeshNode. Instead of Mesh and MeshInstance, this allows more efficient processing of meshes before they are actually registered in the RenderingServer.
-Integrated MeshOptimizer
-Reworked internals of SurfaceTool to use arrays, making it more performant and easy to run optimizatons on.
2020-12-13 21:29:51 -03:00

230 lines
6.5 KiB
C++

// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
#include "meshoptimizer.h"
#include <assert.h>
#include <float.h>
#include <string.h>
// This work is based on:
// Nicolas Capens. Advanced Rasterization. 2004
namespace meshopt
{
const int kViewport = 256;
struct OverdrawBuffer
{
float z[kViewport][kViewport][2];
unsigned int overdraw[kViewport][kViewport][2];
};
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
static float computeDepthGradients(float& dzdx, float& dzdy, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3)
{
// z2 = z1 + dzdx * (x2 - x1) + dzdy * (y2 - y1)
// z3 = z1 + dzdx * (x3 - x1) + dzdy * (y3 - y1)
// (x2-x1 y2-y1)(dzdx) = (z2-z1)
// (x3-x1 y3-y1)(dzdy) (z3-z1)
// we'll solve it with Cramer's rule
float det = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
float invdet = (det == 0) ? 0 : 1 / det;
dzdx = (z2 - z1) * (y3 - y1) - (y2 - y1) * (z3 - z1) * invdet;
dzdy = (x2 - x1) * (z3 - z1) - (z2 - z1) * (x3 - x1) * invdet;
return det;
}
// half-space fixed point triangle rasterizer
static void rasterize(OverdrawBuffer* buffer, float v1x, float v1y, float v1z, float v2x, float v2y, float v2z, float v3x, float v3y, float v3z)
{
// compute depth gradients
float DZx, DZy;
float det = computeDepthGradients(DZx, DZy, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
int sign = det > 0;
// flip backfacing triangles to simplify rasterization logic
if (sign)
{
// flipping v2 & v3 preserves depth gradients since they're based on v1
float t;
t = v2x, v2x = v3x, v3x = t;
t = v2y, v2y = v3y, v3y = t;
t = v2z, v2z = v3z, v3z = t;
// flip depth since we rasterize backfacing triangles to second buffer with reverse Z; only v1z is used below
v1z = kViewport - v1z;
DZx = -DZx;
DZy = -DZy;
}
// coordinates, 28.4 fixed point
int X1 = int(16.0f * v1x + 0.5f);
int X2 = int(16.0f * v2x + 0.5f);
int X3 = int(16.0f * v3x + 0.5f);
int Y1 = int(16.0f * v1y + 0.5f);
int Y2 = int(16.0f * v2y + 0.5f);
int Y3 = int(16.0f * v3y + 0.5f);
// bounding rectangle, clipped against viewport
// since we rasterize pixels with covered centers, min >0.5 should round up
// as for max, due to top-left filling convention we will never rasterize right/bottom edges
// so max >= 0.5 should round down
int minx = max((min(X1, min(X2, X3)) + 7) >> 4, 0);
int maxx = min((max(X1, max(X2, X3)) + 7) >> 4, kViewport);
int miny = max((min(Y1, min(Y2, Y3)) + 7) >> 4, 0);
int maxy = min((max(Y1, max(Y2, Y3)) + 7) >> 4, kViewport);
// deltas, 28.4 fixed point
int DX12 = X1 - X2;
int DX23 = X2 - X3;
int DX31 = X3 - X1;
int DY12 = Y1 - Y2;
int DY23 = Y2 - Y3;
int DY31 = Y3 - Y1;
// fill convention correction
int TL1 = DY12 < 0 || (DY12 == 0 && DX12 > 0);
int TL2 = DY23 < 0 || (DY23 == 0 && DX23 > 0);
int TL3 = DY31 < 0 || (DY31 == 0 && DX31 > 0);
// half edge equations, 24.8 fixed point
// note that we offset minx/miny by half pixel since we want to rasterize pixels with covered centers
int FX = (minx << 4) + 8;
int FY = (miny << 4) + 8;
int CY1 = DX12 * (FY - Y1) - DY12 * (FX - X1) + TL1 - 1;
int CY2 = DX23 * (FY - Y2) - DY23 * (FX - X2) + TL2 - 1;
int CY3 = DX31 * (FY - Y3) - DY31 * (FX - X3) + TL3 - 1;
float ZY = v1z + (DZx * float(FX - X1) + DZy * float(FY - Y1)) * (1 / 16.f);
for (int y = miny; y < maxy; y++)
{
int CX1 = CY1;
int CX2 = CY2;
int CX3 = CY3;
float ZX = ZY;
for (int x = minx; x < maxx; x++)
{
// check if all CXn are non-negative
if ((CX1 | CX2 | CX3) >= 0)
{
if (ZX >= buffer->z[y][x][sign])
{
buffer->z[y][x][sign] = ZX;
buffer->overdraw[y][x][sign]++;
}
}
// signed left shift is UB for negative numbers so use unsigned-signed casts
CX1 -= int(unsigned(DY12) << 4);
CX2 -= int(unsigned(DY23) << 4);
CX3 -= int(unsigned(DY31) << 4);
ZX += DZx;
}
// signed left shift is UB for negative numbers so use unsigned-signed casts
CY1 += int(unsigned(DX12) << 4);
CY2 += int(unsigned(DX23) << 4);
CY3 += int(unsigned(DX31) << 4);
ZY += DZy;
}
}
} // namespace meshopt
meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
{
using namespace meshopt;
assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
assert(vertex_positions_stride % sizeof(float) == 0);
meshopt_Allocator allocator;
size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
meshopt_OverdrawStatistics result = {};
float minv[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
float maxv[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
for (size_t i = 0; i < vertex_count; ++i)
{
const float* v = vertex_positions + i * vertex_stride_float;
for (int j = 0; j < 3; ++j)
{
minv[j] = min(minv[j], v[j]);
maxv[j] = max(maxv[j], v[j]);
}
}
float extent = max(maxv[0] - minv[0], max(maxv[1] - minv[1], maxv[2] - minv[2]));
float scale = kViewport / extent;
float* triangles = allocator.allocate<float>(index_count * 3);
for (size_t i = 0; i < index_count; ++i)
{
unsigned int index = indices[i];
assert(index < vertex_count);
const float* v = vertex_positions + index * vertex_stride_float;
triangles[i * 3 + 0] = (v[0] - minv[0]) * scale;
triangles[i * 3 + 1] = (v[1] - minv[1]) * scale;
triangles[i * 3 + 2] = (v[2] - minv[2]) * scale;
}
OverdrawBuffer* buffer = allocator.allocate<OverdrawBuffer>(1);
for (int axis = 0; axis < 3; ++axis)
{
memset(buffer, 0, sizeof(OverdrawBuffer));
for (size_t i = 0; i < index_count; i += 3)
{
const float* vn0 = &triangles[3 * (i + 0)];
const float* vn1 = &triangles[3 * (i + 1)];
const float* vn2 = &triangles[3 * (i + 2)];
switch (axis)
{
case 0:
rasterize(buffer, vn0[2], vn0[1], vn0[0], vn1[2], vn1[1], vn1[0], vn2[2], vn2[1], vn2[0]);
break;
case 1:
rasterize(buffer, vn0[0], vn0[2], vn0[1], vn1[0], vn1[2], vn1[1], vn2[0], vn2[2], vn2[1]);
break;
case 2:
rasterize(buffer, vn0[1], vn0[0], vn0[2], vn1[1], vn1[0], vn1[2], vn2[1], vn2[0], vn2[2]);
break;
}
}
for (int y = 0; y < kViewport; ++y)
for (int x = 0; x < kViewport; ++x)
for (int s = 0; s < 2; ++s)
{
unsigned int overdraw = buffer->overdraw[y][x][s];
result.pixels_covered += overdraw > 0;
result.pixels_shaded += overdraw;
}
}
result.overdraw = result.pixels_covered ? float(result.pixels_shaded) / float(result.pixels_covered) : 0.f;
return result;
}