mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-11-14 03:55:42 +00:00
Updated meshoptimizer.
This commit is contained in:
+171
-95
@@ -6,11 +6,23 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
// The block below auto-detects SIMD ISA that can be used on the target platform
|
||||
#ifndef MESHOPTIMIZER_NO_SIMD
|
||||
#if defined(__SSE2__) || (defined(_MSC_VER) && defined(_M_X64))
|
||||
#define SIMD_SSE
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__aarch64__) || (defined(_MSC_VER) && defined(_M_ARM64) && _MSC_VER >= 1922)
|
||||
#define SIMD_NEON
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
#endif // !MESHOPTIMIZER_NO_SIMD
|
||||
|
||||
// This work is based on:
|
||||
// Graham Wihlidal. Optimizing the Graphics Pipeline with Compute. 2016
|
||||
// Matthaeus Chajdas. GeometryFX 1.2 - Cluster Culling. 2016
|
||||
// Jack Ritter. An Efficient Bounding Sphere. 1990
|
||||
// Thomas Larsson. Fast and Tight Fitting Bounding Spheres. 2008
|
||||
// Ingo Wald, Vlastimil Havran. On building fast kd-Trees for Ray Tracing, and on doing that in O(N log N). 2006
|
||||
namespace meshopt
|
||||
{
|
||||
|
||||
@@ -148,6 +160,19 @@ static void buildTriangleAdjacencySparse(TriangleAdjacency2& adjacency, const un
|
||||
}
|
||||
}
|
||||
|
||||
static void clearUsed(short* used, size_t vertex_count, const unsigned int* indices, size_t index_count)
|
||||
{
|
||||
// for sparse inputs, it's faster to only clear vertices referenced by the index buffer
|
||||
if (vertex_count <= index_count)
|
||||
memset(used, -1, vertex_count * sizeof(short));
|
||||
else
|
||||
for (size_t i = 0; i < index_count; ++i)
|
||||
{
|
||||
assert(indices[i] < vertex_count);
|
||||
used[indices[i]] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void computeBoundingSphere(float result[4], const float* points, size_t count, size_t points_stride, const float* radii, size_t radii_stride, size_t axis_count)
|
||||
{
|
||||
static const float kAxes[7][3] = {
|
||||
@@ -264,21 +289,8 @@ struct Cone
|
||||
float nx, ny, nz;
|
||||
};
|
||||
|
||||
static float getDistance(float dx, float dy, float dz, bool aa)
|
||||
{
|
||||
if (!aa)
|
||||
return sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
float rx = fabsf(dx), ry = fabsf(dy), rz = fabsf(dz);
|
||||
float rxy = rx > ry ? rx : ry;
|
||||
return rxy > rz ? rxy : rz;
|
||||
}
|
||||
|
||||
static float getMeshletScore(float distance, float spread, float cone_weight, float expected_radius)
|
||||
{
|
||||
if (cone_weight < 0)
|
||||
return 1 + distance / expected_radius;
|
||||
|
||||
float cone = 1.f - spread * cone_weight;
|
||||
float cone_clamped = cone < 1e-3f ? 1e-3f : cone;
|
||||
|
||||
@@ -347,15 +359,6 @@ static float computeTriangleCones(Cone* triangles, const unsigned int* indices,
|
||||
return mesh_area;
|
||||
}
|
||||
|
||||
static void finishMeshlet(meshopt_Meshlet& meshlet, unsigned char* meshlet_triangles)
|
||||
{
|
||||
size_t offset = meshlet.triangle_offset + meshlet.triangle_count * 3;
|
||||
|
||||
// fill 4b padding with 0
|
||||
while (offset & 3)
|
||||
meshlet_triangles[offset++] = 0;
|
||||
}
|
||||
|
||||
static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, short* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles, bool split = false)
|
||||
{
|
||||
short& av = used[a];
|
||||
@@ -373,10 +376,8 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
|
||||
for (size_t j = 0; j < meshlet.vertex_count; ++j)
|
||||
used[meshlet_vertices[meshlet.vertex_offset + j]] = -1;
|
||||
|
||||
finishMeshlet(meshlet, meshlet_triangles);
|
||||
|
||||
meshlet.vertex_offset += meshlet.vertex_count;
|
||||
meshlet.triangle_offset += (meshlet.triangle_count * 3 + 3) & ~3; // 4b padding
|
||||
meshlet.triangle_offset += meshlet.triangle_count * 3;
|
||||
meshlet.vertex_count = 0;
|
||||
meshlet.triangle_count = 0;
|
||||
|
||||
@@ -452,7 +453,7 @@ static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Co
|
||||
const Cone& tri_cone = triangles[triangle];
|
||||
|
||||
float dx = tri_cone.px - meshlet_cone.px, dy = tri_cone.py - meshlet_cone.py, dz = tri_cone.pz - meshlet_cone.pz;
|
||||
float distance = getDistance(dx, dy, dz, cone_weight < 0);
|
||||
float distance = sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
float spread = tri_cone.nx * meshlet_cone.nx + tri_cone.ny * meshlet_cone.ny + tri_cone.nz * meshlet_cone.nz;
|
||||
|
||||
float score = getMeshletScore(distance, spread, cone_weight, meshlet_expected_radius);
|
||||
@@ -513,7 +514,8 @@ static size_t appendSeedTriangles(unsigned int* seeds, const meshopt_Meshlet& me
|
||||
if (best_neighbor == ~0u)
|
||||
continue;
|
||||
|
||||
float best_neighbor_score = getDistance(triangles[best_neighbor].px - cornerx, triangles[best_neighbor].py - cornery, triangles[best_neighbor].pz - cornerz, false);
|
||||
float dx = triangles[best_neighbor].px - cornerx, dy = triangles[best_neighbor].py - cornery, dz = triangles[best_neighbor].pz - cornerz;
|
||||
float best_neighbor_score = sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
for (size_t j = 0; j < kMeshletAddSeeds; ++j)
|
||||
{
|
||||
@@ -565,7 +567,8 @@ static unsigned int selectSeedTriangle(const unsigned int* seeds, size_t seed_co
|
||||
unsigned int a = indices[index * 3 + 0], b = indices[index * 3 + 1], c = indices[index * 3 + 2];
|
||||
|
||||
unsigned int live = live_triangles[a] + live_triangles[b] + live_triangles[c];
|
||||
float score = getDistance(triangles[index].px - cornerx, triangles[index].py - cornery, triangles[index].pz - cornerz, false);
|
||||
float dx = triangles[index].px - cornerx, dy = triangles[index].py - cornery, dz = triangles[index].pz - cornerz;
|
||||
float score = sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (live < best_live || (live == best_live && score < best_score))
|
||||
{
|
||||
@@ -586,7 +589,7 @@ struct KDNode
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
// leaves: axis = 3, children = number of extra points after this one (0 if 'index' is the only point)
|
||||
// leaves: axis = 3, children = number of points including this one
|
||||
// branches: axis != 3, left subtree = skip 1, right subtree = skip 1+children
|
||||
unsigned int axis : 2;
|
||||
unsigned int children : 30;
|
||||
@@ -622,7 +625,7 @@ static size_t kdtreeBuildLeaf(size_t offset, KDNode* nodes, size_t node_count, u
|
||||
|
||||
result.index = indices[0];
|
||||
result.axis = 3;
|
||||
result.children = unsigned(count - 1);
|
||||
result.children = unsigned(count);
|
||||
|
||||
// all remaining points are stored in nodes immediately following the leaf
|
||||
for (size_t i = 1; i < count; ++i)
|
||||
@@ -681,29 +684,37 @@ static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const
|
||||
size_t next_offset = kdtreeBuild(offset + 1, nodes, node_count, points, stride, indices, middle, leaf_size);
|
||||
|
||||
// distance to the right subtree is represented explicitly
|
||||
assert(next_offset - offset > 1);
|
||||
result.children = unsigned(next_offset - offset - 1);
|
||||
|
||||
return kdtreeBuild(next_offset, nodes, node_count, points, stride, indices + middle, count - middle, leaf_size);
|
||||
}
|
||||
|
||||
static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points, size_t stride, const unsigned char* emitted_flags, const float* position, bool aa, unsigned int& result, float& limit)
|
||||
static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points, size_t stride, const unsigned char* emitted_flags, const float* position, unsigned int& result, float& limit)
|
||||
{
|
||||
const KDNode& node = nodes[root];
|
||||
|
||||
if (node.children == 0)
|
||||
return;
|
||||
|
||||
if (node.axis == 3)
|
||||
{
|
||||
// leaf
|
||||
for (unsigned int i = 0; i <= node.children; ++i)
|
||||
bool inactive = true;
|
||||
|
||||
for (unsigned int i = 0; i < node.children; ++i)
|
||||
{
|
||||
unsigned int index = nodes[root + i].index;
|
||||
|
||||
if (emitted_flags[index])
|
||||
continue;
|
||||
|
||||
inactive = false;
|
||||
|
||||
const float* point = points + index * stride;
|
||||
|
||||
float dx = point[0] - position[0], dy = point[1] - position[1], dz = point[2] - position[2];
|
||||
float distance = getDistance(dx, dy, dz, aa);
|
||||
float distance = sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (distance < limit)
|
||||
{
|
||||
@@ -711,6 +722,10 @@ static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points,
|
||||
limit = distance;
|
||||
}
|
||||
}
|
||||
|
||||
// deactivate leaves that no longer have items to emit
|
||||
if (inactive)
|
||||
nodes[root].children = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -719,34 +734,84 @@ static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points,
|
||||
unsigned int first = (delta <= 0) ? 0 : node.children;
|
||||
unsigned int second = first ^ node.children;
|
||||
|
||||
kdtreeNearest(nodes, root + 1 + first, points, stride, emitted_flags, position, aa, result, limit);
|
||||
// deactivate branches that no longer have items to emit to accelerate traversal
|
||||
// note that we do this *before* recursing which delays deactivation but keeps tail calls
|
||||
if ((nodes[root + 1 + first].children | nodes[root + 1 + second].children) == 0)
|
||||
nodes[root].children = 0;
|
||||
|
||||
kdtreeNearest(nodes, root + 1 + first, points, stride, emitted_flags, position, result, limit);
|
||||
|
||||
// only process the other node if it can have a match based on closest distance so far
|
||||
if (fabsf(delta) <= limit)
|
||||
kdtreeNearest(nodes, root + 1 + second, points, stride, emitted_flags, position, aa, result, limit);
|
||||
kdtreeNearest(nodes, root + 1 + second, points, stride, emitted_flags, position, result, limit);
|
||||
}
|
||||
}
|
||||
|
||||
struct BVHBoxT
|
||||
{
|
||||
float min[4];
|
||||
float max[4];
|
||||
};
|
||||
|
||||
struct BVHBox
|
||||
{
|
||||
float min[3];
|
||||
float max[3];
|
||||
};
|
||||
|
||||
static void boxMerge(BVHBox& box, const BVHBox& other)
|
||||
#if defined(SIMD_SSE)
|
||||
static float boxMerge(BVHBoxT& box, const BVHBox& other)
|
||||
{
|
||||
__m128 min = _mm_loadu_ps(box.min);
|
||||
__m128 max = _mm_loadu_ps(box.max);
|
||||
|
||||
min = _mm_min_ps(min, _mm_loadu_ps(other.min));
|
||||
max = _mm_max_ps(max, _mm_loadu_ps(other.max));
|
||||
|
||||
_mm_store_ps(box.min, min);
|
||||
_mm_store_ps(box.max, max);
|
||||
|
||||
__m128 size = _mm_sub_ps(max, min);
|
||||
__m128 size_yzx = _mm_shuffle_ps(size, size, _MM_SHUFFLE(0, 0, 2, 1));
|
||||
__m128 mul = _mm_mul_ps(size, size_yzx);
|
||||
__m128 sum_xy = _mm_add_ss(mul, _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
__m128 sum_xyz = _mm_add_ss(sum_xy, _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 2, 2, 2)));
|
||||
|
||||
return _mm_cvtss_f32(sum_xyz);
|
||||
}
|
||||
#elif defined(SIMD_NEON)
|
||||
static float boxMerge(BVHBoxT& box, const BVHBox& other)
|
||||
{
|
||||
float32x4_t min = vld1q_f32(box.min);
|
||||
float32x4_t max = vld1q_f32(box.max);
|
||||
|
||||
min = vminq_f32(min, vld1q_f32(other.min));
|
||||
max = vmaxq_f32(max, vld1q_f32(other.max));
|
||||
|
||||
vst1q_f32(box.min, min);
|
||||
vst1q_f32(box.max, max);
|
||||
|
||||
float32x4_t size = vsubq_f32(max, min);
|
||||
float32x4_t size_yzx = vextq_f32(vextq_f32(size, size, 3), size, 2);
|
||||
float32x4_t mul = vmulq_f32(size, size_yzx);
|
||||
float sum_xy = vgetq_lane_f32(mul, 0) + vgetq_lane_f32(mul, 1);
|
||||
float sum_xyz = sum_xy + vgetq_lane_f32(mul, 2);
|
||||
|
||||
return sum_xyz;
|
||||
}
|
||||
#else
|
||||
static float boxMerge(BVHBoxT& box, const BVHBox& other)
|
||||
{
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
box.min[k] = other.min[k] < box.min[k] ? other.min[k] : box.min[k];
|
||||
box.max[k] = other.max[k] > box.max[k] ? other.max[k] : box.max[k];
|
||||
}
|
||||
}
|
||||
|
||||
inline float boxSurface(const BVHBox& box)
|
||||
{
|
||||
float sx = box.max[0] - box.min[0], sy = box.max[1] - box.min[1], sz = box.max[2] - box.min[2];
|
||||
return sx * sy + sx * sz + sy * sz;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline unsigned int radixFloat(unsigned int v)
|
||||
{
|
||||
@@ -832,7 +897,7 @@ static void bvhPrepare(BVHBox* boxes, float* centroids, const unsigned int* indi
|
||||
}
|
||||
}
|
||||
|
||||
static bool bvhPackLeaf(unsigned char* boundary, const unsigned int* order, size_t count, short* used, const unsigned int* indices, size_t max_vertices)
|
||||
static size_t bvhCountVertices(const unsigned int* order, size_t count, short* used, const unsigned int* indices, unsigned int* out = NULL)
|
||||
{
|
||||
// count number of unique vertices
|
||||
size_t used_vertices = 0;
|
||||
@@ -843,6 +908,9 @@ static bool bvhPackLeaf(unsigned char* boundary, const unsigned int* order, size
|
||||
|
||||
used_vertices += (used[a] < 0) + (used[b] < 0) + (used[c] < 0);
|
||||
used[a] = used[b] = used[c] = 1;
|
||||
|
||||
if (out)
|
||||
out[i] = unsigned(used_vertices);
|
||||
}
|
||||
|
||||
// reset used[] for future invocations
|
||||
@@ -854,16 +922,16 @@ static bool bvhPackLeaf(unsigned char* boundary, const unsigned int* order, size
|
||||
used[a] = used[b] = used[c] = -1;
|
||||
}
|
||||
|
||||
if (used_vertices > max_vertices)
|
||||
return false;
|
||||
return used_vertices;
|
||||
}
|
||||
|
||||
static void bvhPackLeaf(unsigned char* boundary, size_t count)
|
||||
{
|
||||
// mark meshlet boundary for future reassembly
|
||||
assert(count > 0);
|
||||
|
||||
boundary[0] = 1;
|
||||
memset(boundary + 1, 0, count - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bvhPackTail(unsigned char* boundary, const unsigned int* order, size_t count, short* used, const unsigned int* indices, size_t max_vertices, size_t max_triangles)
|
||||
@@ -872,8 +940,9 @@ static void bvhPackTail(unsigned char* boundary, const unsigned int* order, size
|
||||
{
|
||||
size_t chunk = i + max_triangles <= count ? max_triangles : count - i;
|
||||
|
||||
if (bvhPackLeaf(boundary + i, order + i, chunk, used, indices, max_vertices))
|
||||
if (bvhCountVertices(order + i, chunk, used, indices) <= max_vertices)
|
||||
{
|
||||
bvhPackLeaf(boundary + i, chunk);
|
||||
i += chunk;
|
||||
continue;
|
||||
}
|
||||
@@ -881,7 +950,7 @@ static void bvhPackTail(unsigned char* boundary, const unsigned int* order, size
|
||||
// chunk is vertex bound, split it into smaller meshlets
|
||||
assert(chunk > max_vertices / 3);
|
||||
|
||||
bvhPackLeaf(boundary + i, order + i, max_vertices / 3, used, indices, max_vertices);
|
||||
bvhPackLeaf(boundary + i, max_vertices / 3);
|
||||
i += max_vertices / 3;
|
||||
}
|
||||
}
|
||||
@@ -889,30 +958,32 @@ static void bvhPackTail(unsigned char* boundary, const unsigned int* order, size
|
||||
static bool bvhDivisible(size_t count, size_t min, size_t max)
|
||||
{
|
||||
// count is representable as a sum of values in [min..max] if if it in range of [k*min..k*min+k*(max-min)]
|
||||
// equivalent to ceil(count / max) <= floor(count / min), but the form below allows using idiv
|
||||
// equivalent to ceil(count / max) <= floor(count / min), but the form below allows using idiv (see nv_cluster_builder)
|
||||
// we avoid expensive integer divisions in the common case where min is <= max/2
|
||||
return min * 2 <= max ? count >= min : count % min <= (count / min) * (max - min);
|
||||
}
|
||||
|
||||
static size_t bvhPivot(const BVHBox* boxes, const unsigned int* order, size_t count, void* scratch, size_t step, size_t min, size_t max, float fill, float* out_cost)
|
||||
static void bvhComputeArea(float* areas, const BVHBox* boxes, const unsigned int* order, size_t count)
|
||||
{
|
||||
BVHBox accuml = boxes[order[0]], accumr = boxes[order[count - 1]];
|
||||
float* costs = static_cast<float*>(scratch);
|
||||
BVHBoxT accuml = {{FLT_MAX, FLT_MAX, FLT_MAX, 0}, {-FLT_MAX, -FLT_MAX, -FLT_MAX, 0}};
|
||||
BVHBoxT accumr = accuml;
|
||||
|
||||
// accumulate SAH cost in forward and backward directions
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
boxMerge(accuml, boxes[order[i]]);
|
||||
boxMerge(accumr, boxes[order[count - 1 - i]]);
|
||||
float larea = boxMerge(accuml, boxes[order[i]]);
|
||||
float rarea = boxMerge(accumr, boxes[order[count - 1 - i]]);
|
||||
|
||||
costs[i] = boxSurface(accuml);
|
||||
costs[i + count] = boxSurface(accumr);
|
||||
areas[i] = larea;
|
||||
areas[i + count] = rarea;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t bvhPivot(const float* areas, const unsigned int* vertices, size_t count, size_t step, size_t min, size_t max, float fill, size_t maxfill, float* out_cost)
|
||||
{
|
||||
bool aligned = count >= min * 2 && bvhDivisible(count, min, max);
|
||||
size_t end = aligned ? count - min : count - 1;
|
||||
|
||||
float rmaxf = 1.f / float(int(max));
|
||||
float rmaxfill = 1.f / float(int(maxfill));
|
||||
|
||||
// find best split that minimizes SAH
|
||||
size_t bestsplit = 0;
|
||||
@@ -927,17 +998,22 @@ static size_t bvhPivot(const BVHBox* boxes, const unsigned int* order, size_t co
|
||||
if (aligned && !bvhDivisible(rsplit, min, max))
|
||||
continue;
|
||||
|
||||
// costs[x] = inclusive surface area of boxes[0..x]
|
||||
// costs[count-1-x] = inclusive surface area of boxes[x..count-1]
|
||||
float larea = costs[i], rarea = costs[(count - 1 - (i + 1)) + count];
|
||||
// areas[x] = inclusive surface area of boxes[0..x]
|
||||
// areas[count-1-x] = inclusive surface area of boxes[x..count-1]
|
||||
float larea = areas[i], rarea = areas[(count - 1 - (i + 1)) + count];
|
||||
float cost = larea * float(int(lsplit)) + rarea * float(int(rsplit));
|
||||
|
||||
if (cost > bestcost)
|
||||
continue;
|
||||
|
||||
// fill cost; use floating point math to avoid expensive integer modulo
|
||||
int lrest = int(float(int(lsplit + max - 1)) * rmaxf) * int(max) - int(lsplit);
|
||||
int rrest = int(float(int(rsplit + max - 1)) * rmaxf) * int(max) - int(rsplit);
|
||||
// use vertex fill when splitting vertex limited clusters; note that we use the same (left->right) vertex count
|
||||
// using bidirectional vertex counts is a little more expensive to compute and produces slightly worse results in practice
|
||||
size_t lfill = vertices ? vertices[i] : lsplit;
|
||||
size_t rfill = vertices ? vertices[i] : rsplit;
|
||||
|
||||
// fill cost; use floating point math to round up to maxfill to avoid expensive integer modulo
|
||||
int lrest = int(float(int(lfill + maxfill - 1)) * rmaxfill) * int(maxfill) - int(lfill);
|
||||
int rrest = int(float(int(rfill + maxfill - 1)) * rmaxfill) * int(maxfill) - int(rfill);
|
||||
|
||||
cost += fill * (float(lrest) * larea + float(rrest) * rarea);
|
||||
|
||||
@@ -973,8 +1049,8 @@ static void bvhSplit(const BVHBox* boxes, unsigned int* orderx, unsigned int* or
|
||||
if (depth >= kMeshletMaxTreeDepth)
|
||||
return bvhPackTail(boundary, orderx, count, used, indices, max_vertices, max_triangles);
|
||||
|
||||
if (count <= max_triangles && bvhPackLeaf(boundary, orderx, count, used, indices, max_vertices))
|
||||
return;
|
||||
if (count <= max_triangles && bvhCountVertices(orderx, count, used, indices) <= max_vertices)
|
||||
return bvhPackLeaf(boundary, count);
|
||||
|
||||
unsigned int* axes[3] = {orderx, ordery, orderz};
|
||||
|
||||
@@ -983,9 +1059,7 @@ static void bvhSplit(const BVHBox* boxes, unsigned int* orderx, unsigned int* or
|
||||
|
||||
// if we could not pack the meshlet, we must be vertex bound
|
||||
size_t mint = count <= max_triangles && max_vertices / 3 < min_triangles ? max_vertices / 3 : min_triangles;
|
||||
|
||||
// only use fill weight if we are optimizing for triangle count
|
||||
float fill = count <= max_triangles ? 0.f : fill_weight;
|
||||
size_t maxfill = count <= max_triangles ? max_vertices : max_triangles;
|
||||
|
||||
// find best split that minimizes SAH
|
||||
int bestk = -1;
|
||||
@@ -994,8 +1068,20 @@ static void bvhSplit(const BVHBox* boxes, unsigned int* orderx, unsigned int* or
|
||||
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
float* areas = static_cast<float*>(scratch);
|
||||
unsigned int* vertices = NULL;
|
||||
|
||||
bvhComputeArea(areas, boxes, axes[k], count);
|
||||
|
||||
if (count <= max_triangles)
|
||||
{
|
||||
// for vertex bound clusters, count number of unique vertices for each split
|
||||
vertices = reinterpret_cast<unsigned int*>(areas + 2 * count);
|
||||
bvhCountVertices(axes[k], count, used, indices, vertices);
|
||||
}
|
||||
|
||||
float axiscost = FLT_MAX;
|
||||
size_t axissplit = bvhPivot(boxes, axes[k], count, scratch, step, mint, max_triangles, fill, &axiscost);
|
||||
size_t axissplit = bvhPivot(areas, vertices, count, step, mint, max_triangles, fill_weight, maxfill, &axiscost);
|
||||
|
||||
if (axissplit && axiscost < bestcost)
|
||||
{
|
||||
@@ -1044,7 +1130,6 @@ size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_
|
||||
assert(index_count % 3 == 0);
|
||||
assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
|
||||
assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
|
||||
assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
|
||||
|
||||
(void)kMeshletMaxVertices;
|
||||
(void)kMeshletMaxTriangles;
|
||||
@@ -1069,9 +1154,8 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
|
||||
assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
|
||||
assert(min_triangles >= 1 && min_triangles <= max_triangles && max_triangles <= kMeshletMaxTriangles);
|
||||
assert(min_triangles % 4 == 0 && max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
|
||||
|
||||
assert(cone_weight <= 1); // negative cone weight switches metric to optimize for axis-aligned meshlets
|
||||
assert(cone_weight >= 0 && cone_weight <= 1);
|
||||
assert(split_factor >= 0);
|
||||
|
||||
if (index_count == 0)
|
||||
@@ -1123,7 +1207,7 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
|
||||
// index of the vertex in the meshlet, -1 if the vertex isn't used
|
||||
short* used = allocator.allocate<short>(vertex_count);
|
||||
memset(used, -1, vertex_count * sizeof(short));
|
||||
clearUsed(used, vertex_count, indices, index_count);
|
||||
|
||||
// initial seed triangle is the one closest to the corner
|
||||
unsigned int initial_seed = ~0u;
|
||||
@@ -1133,7 +1217,8 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
{
|
||||
const Cone& tri = triangles[i];
|
||||
|
||||
float score = getDistance(tri.px - cornerx, tri.py - cornery, tri.pz - cornerz, false);
|
||||
float dx = tri.px - cornerx, dy = tri.py - cornery, dz = tri.pz - cornerz;
|
||||
float score = sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (initial_seed == ~0u || score < initial_score)
|
||||
{
|
||||
@@ -1173,7 +1258,7 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
unsigned int index = ~0u;
|
||||
float distance = FLT_MAX;
|
||||
|
||||
kdtreeNearest(nodes, 0, &triangles[0].px, sizeof(Cone) / sizeof(float), emitted_flags, position, cone_weight < 0.f, index, distance);
|
||||
kdtreeNearest(nodes, 0, &triangles[0].px, sizeof(Cone) / sizeof(float), emitted_flags, position, index, distance);
|
||||
|
||||
best_triangle = index;
|
||||
split = meshlet.triangle_count >= min_triangles && split_factor > 0 && distance > meshlet_expected_radius * split_factor;
|
||||
@@ -1243,20 +1328,15 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
}
|
||||
|
||||
if (meshlet.triangle_count)
|
||||
{
|
||||
finishMeshlet(meshlet, meshlet_triangles);
|
||||
|
||||
meshlets[meshlet_offset++] = meshlet;
|
||||
}
|
||||
|
||||
assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, min_triangles));
|
||||
assert(meshlet.triangle_offset + meshlet.triangle_count * 3 <= index_count && meshlet.vertex_offset + meshlet.vertex_count <= index_count);
|
||||
return meshlet_offset;
|
||||
}
|
||||
|
||||
size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight)
|
||||
{
|
||||
assert(cone_weight >= 0); // to use negative cone weight, use meshopt_buildMeshletsFlex
|
||||
|
||||
return meshopt_buildMeshletsFlex(meshlets, meshlet_vertices, meshlet_triangles, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, max_triangles, cone_weight, 0.0f);
|
||||
}
|
||||
|
||||
@@ -1268,13 +1348,12 @@ size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
|
||||
assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
|
||||
assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
|
||||
assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
|
||||
|
||||
meshopt_Allocator allocator;
|
||||
|
||||
// index of the vertex in the meshlet, -1 if the vertex isn't used
|
||||
short* used = allocator.allocate<short>(vertex_count);
|
||||
memset(used, -1, vertex_count * sizeof(short));
|
||||
clearUsed(used, vertex_count, indices, index_count);
|
||||
|
||||
meshopt_Meshlet meshlet = {};
|
||||
size_t meshlet_offset = 0;
|
||||
@@ -1289,17 +1368,14 @@ size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshle
|
||||
}
|
||||
|
||||
if (meshlet.triangle_count)
|
||||
{
|
||||
finishMeshlet(meshlet, meshlet_triangles);
|
||||
|
||||
meshlets[meshlet_offset++] = meshlet;
|
||||
}
|
||||
|
||||
assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles));
|
||||
assert(meshlet.triangle_offset + meshlet.triangle_count * 3 <= index_count && meshlet.vertex_offset + meshlet.vertex_count <= index_count);
|
||||
return meshlet_offset;
|
||||
}
|
||||
|
||||
size_t meshopt_buildMeshletsSplit(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float fill_weight)
|
||||
size_t meshopt_buildMeshletsSpatial(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float fill_weight)
|
||||
{
|
||||
using namespace meshopt;
|
||||
|
||||
@@ -1309,7 +1385,6 @@ size_t meshopt_buildMeshletsSplit(struct meshopt_Meshlet* meshlets, unsigned int
|
||||
|
||||
assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
|
||||
assert(min_triangles >= 1 && min_triangles <= max_triangles && max_triangles <= kMeshletMaxTriangles);
|
||||
assert(min_triangles % 4 == 0 && max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
|
||||
|
||||
if (index_count == 0)
|
||||
return 0;
|
||||
@@ -1320,13 +1395,14 @@ size_t meshopt_buildMeshletsSplit(struct meshopt_Meshlet* meshlets, unsigned int
|
||||
meshopt_Allocator allocator;
|
||||
|
||||
// 3 floats plus 1 uint for sorting, or
|
||||
// 2 floats for SAH costs, or
|
||||
// 2 floats plus 1 uint for pivoting, or
|
||||
// 1 uint plus 1 byte for partitioning
|
||||
float* scratch = allocator.allocate<float>(face_count * 4);
|
||||
|
||||
// compute bounding boxes and centroids for sorting
|
||||
BVHBox* boxes = allocator.allocate<BVHBox>(face_count);
|
||||
BVHBox* boxes = allocator.allocate<BVHBox>(face_count + 1); // padding for SIMD
|
||||
bvhPrepare(boxes, scratch, indices, face_count, vertex_positions, vertex_count, vertex_stride_float);
|
||||
memset(boxes + face_count, 0, sizeof(BVHBox));
|
||||
|
||||
unsigned int* axes = allocator.allocate<unsigned int>(face_count * 3);
|
||||
unsigned int* temp = reinterpret_cast<unsigned int*>(scratch) + face_count * 3;
|
||||
@@ -1350,7 +1426,7 @@ size_t meshopt_buildMeshletsSplit(struct meshopt_Meshlet* meshlets, unsigned int
|
||||
|
||||
// index of the vertex in the meshlet, -1 if the vertex isn't used
|
||||
short* used = allocator.allocate<short>(vertex_count);
|
||||
memset(used, -1, vertex_count * sizeof(short));
|
||||
clearUsed(used, vertex_count, indices, index_count);
|
||||
|
||||
unsigned char* boundary = allocator.allocate<unsigned char>(face_count);
|
||||
|
||||
@@ -1391,13 +1467,10 @@ size_t meshopt_buildMeshletsSplit(struct meshopt_Meshlet* meshlets, unsigned int
|
||||
}
|
||||
|
||||
if (meshlet.triangle_count)
|
||||
{
|
||||
finishMeshlet(meshlet, meshlet_triangles);
|
||||
|
||||
meshlets[meshlet_offset++] = meshlet;
|
||||
}
|
||||
|
||||
assert(meshlet_offset <= meshlet_bound);
|
||||
assert(meshlet.triangle_offset + meshlet.triangle_count * 3 <= index_count && meshlet.vertex_offset + meshlet.vertex_count <= index_count);
|
||||
return meshlet_offset;
|
||||
}
|
||||
|
||||
@@ -1693,3 +1766,6 @@ void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* mesh
|
||||
assert(vertex_offset <= vertex_count);
|
||||
memcpy(vertices, order, vertex_offset * sizeof(unsigned int));
|
||||
}
|
||||
|
||||
#undef SIMD_SSE
|
||||
#undef SIMD_NEON
|
||||
|
||||
Reference in New Issue
Block a user