Files
2026-08-16 18:33:03 +07:00

82 lines
3.2 KiB
C++

#pragma once
#include <cstddef>
#include <cmath>
#include <cstdint>
#include <unordered_map>
namespace t2pbr {
inline uint64_t voxel_key(int32_t x, int32_t y, int32_t z) {
return ((uint64_t) (uint32_t) x << 42) |
((uint64_t) (uint32_t) y << 21) |
(uint64_t) (uint32_t) z;
}
// Trilinearly sample a sparse voxel field. query_vox is expressed in voxel
// coordinates, exactly like upstream's (xyz - origin) / voxel_size. Missing
// corners do not contribute and present weights are renormalized, matching the
// reference helper used for per-vertex texture validation.
inline void sample_sparse_trilinear(const float * feats, int n_voxels, int channels,
const int32_t * coords,
const float * query_vox, int n_queries,
float * out, float * out_weights = nullptr) {
std::unordered_map<uint64_t, int> rows;
rows.reserve((size_t) n_voxels * 2);
for (int i = 0; i < n_voxels; ++i) {
rows[voxel_key(coords[(size_t) i * 3], coords[(size_t) i * 3 + 1],
coords[(size_t) i * 3 + 2])] = i;
}
for (int q = 0; q < n_queries; ++q) {
float * dst = out + (size_t) q * channels;
for (int c = 0; c < channels; ++c) dst[c] = 0.0f;
const float * p = query_vox + (size_t) q * 3;
const int32_t bx = (int32_t) std::floor(p[0]);
const int32_t by = (int32_t) std::floor(p[1]);
const int32_t bz = (int32_t) std::floor(p[2]);
const float fx = p[0] - bx, fy = p[1] - by, fz = p[2] - bz;
float wsum = 0.0f;
for (int dx = 0; dx <= 1; ++dx)
for (int dy = 0; dy <= 1; ++dy)
for (int dz = 0; dz <= 1; ++dz) {
const float w = (dx ? fx : 1.0f - fx) *
(dy ? fy : 1.0f - fy) *
(dz ? fz : 1.0f - fz);
if (w == 0.0f) continue;
auto it = rows.find(voxel_key(bx + dx, by + dy, bz + dz));
if (it == rows.end()) continue;
const float * src = feats + (size_t) it->second * channels;
for (int c = 0; c < channels; ++c) dst[c] += w * src[c];
wsum += w;
}
if (wsum > 1e-6f) {
const float inv = 1.0f / wsum;
for (int c = 0; c < channels; ++c) dst[c] *= inv;
}
if (out_weights) out_weights[q] = wsum;
}
}
// Detect the characteristic failed texture decode where nearly every vertex
// has every material channel pinned to one. Requiring all channels avoids
// rejecting legitimate white, fully opaque, metallic, or rough materials.
inline bool is_collapsed_saturated(const float * pbr, int n_vertices,
int channels = 6, float threshold = 0.999f,
int permille = 995) {
if (!pbr || n_vertices <= 0 || channels <= 0) return false;
size_t saturated = 0;
for (int v = 0; v < n_vertices; ++v) {
bool all_one = true;
for (int c = 0; c < channels; ++c) {
all_one = all_one && pbr[(size_t) v * channels + c] >= threshold;
}
saturated += all_one;
}
return saturated * 1000 >= (size_t) n_vertices * permille;
}
} // namespace t2pbr