19 static Mat4 identity() {
21 r.m[0] = r.m[5] = r.m[10] = r.m[15] = 1.f;
24 static Mat4 mul(
const Mat4 &
a,
const Mat4 &
b) {
26 for (
int c = 0;
c < 4; ++
c)
27 for (
int row = 0;
row < 4; ++
row) {
29 for (
int k = 0; k < 4; ++k) sum +=
a.m[k * 4 + row] *
b.m[
c * 4 + k];
30 r.m[
c * 4 +
row] = sum;
44 bool ok = builder.
build(in, builderOptions_, asset_);
45 if (!
ok)
return false;
46 vgReset(backend_, visibleCapacity_);
52 const std::uint32_t *indices,
int indexCount) {
63void buildIco(std::vector<float> &positions, std::vector<std::uint32_t> &indices,
int subdiv) {
66 struct F {
int a,
b,
c; };
67 auto addVtx = [&](
float x,
float y,
float z) {
68 float l = std::sqrt(
x *
x +
y *
y +
z *
z);
69 positions.push_back(
x / l);
70 positions.push_back(
y / l);
71 positions.push_back(
z / l);
72 return static_cast<int>(positions.size() / 3) - 1;
74 auto tri = [&](
int a,
int b,
int c) {
75 indices.push_back(
static_cast<std::uint32_t
>(
a));
76 indices.push_back(
static_cast<std::uint32_t
>(
b));
77 indices.push_back(
static_cast<std::uint32_t
>(
c));
79 auto mid = [&](
int i0,
int i1) {
80 return addVtx(0.5f * (positions[3 *
i0] + positions[3 *
i1]),
81 0.5f * (positions[3 *
i0 + 1] + positions[3 *
i1 + 1]),
82 0.5f * (positions[3 *
i0 + 2] + positions[3 *
i1 + 2]));
84 const float t = (1.f + std::sqrt(5.f)) * 0.5f;
85 std::vector<std::array<float, 3>> base = {
86 {-1, t, 0}, {1, t, 0}, {-1, -t, 0}, {1, -t, 0}, {0, -1, t}, {0, 1, t},
87 {0, -1, -t}, {0, 1, -t}, {t, 0, -1}, {t, 0, 1}, {-t, 0, -1}, {-t, 0, 1}};
89 for (
int i = 0; i < 12; ++i)
idx[i] = addVtx(base[i][0], base[i][1], base[i][2]);
90 int faces[20][3] = {{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
91 {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
92 {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
93 {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}};
95 for (
auto &
f : faces) cur.push_back({
idx[
f[0]],
idx[
f[1]],
idx[
f[2]]});
96 for (
int d = 0;
d < subdiv; ++
d) {
99 int ab = mid(
x.a,
x.b), ac = mid(
x.a,
x.c), bc = mid(
x.b,
x.c);
100 nf.push_back({
x.a, ab, ac});
101 nf.push_back({ab,
x.b, bc});
102 nf.push_back({ac, bc,
x.c});
103 nf.push_back({ab, bc, ac});
107 for (
auto &
x : cur) tri(
x.
a,
x.
b,
x.
c);
112 std::vector<float>
pos;
113 std::vector<std::uint32_t>
idx;
114 buildIco(
pos,
idx, std::clamp(subdiv, 1, 6));
115 return build(
pos.data(),
static_cast<int>(
pos.size() / 3),
idx.data(),
116 static_cast<int>(
idx.size()));
120 width_ = std::max(1,
width);
121 height_ = std::max(1,
height);
122 uniforms_.
params[0] =
static_cast<float>(width_);
123 uniforms_.
params[1] =
static_cast<float>(height_);
124 uniforms_.
params[2] =
static_cast<float>(height_) /
125 (2.f * std::tan(fovYDeg * 3.14159265358979323846f / 360.f));
126 uniforms_.
params[3] = errorPx;
130 const float model[16],
const float camPos[3]) {
134 for (
int i = 0; i < 16; ++i)
p.m[i] =
proj[i];
136 for (
int i = 0; i < 16; ++i)
v.m[i] =
view[i];
137 Mat4 vp = Mat4::mul(
p,
v);
138 for (
int i = 0; i < 16; ++i) uniforms_.
viewProj[i] = vp.m[i];
139 for (
int i = 0; i < 16; ++i) uniforms_.
model[i] =
model ?
model[i] : Mat4::identity().m[i];
149 const float fovY = (uniforms_.
params[2] > 0.f)
150 ? 2.f * std::atan(
static_cast<float>(height_) / (2.f * uniforms_.
params[2]))
151 : 60.f * 3.14159265358979323846f / 180.f;
153 Mat4
view = Mat4::identity();
156 const float a = 1.f / std::tan(fovY * 0.5f);
157 const float aspect =
static_cast<float>(width_) /
static_cast<float>(height_);
158 proj.m[0] =
a / aspect;
160 proj.m[10] = farZ / (nearZ - farZ);
162 proj.m[14] = (nearZ * farZ) / (nearZ - farZ);
163 const float camPos[3] = {camX, camY, camZ};
169void VirtualGeometryRenderer::updateUniforms() {
170 uniforms_.
misc[0] =
static_cast<float>(
static_cast<int>(asset_.
clusters.size()));
171 if (modelYaw_ != 0.f) {
173 float c = std::cos(modelYaw_),
s = std::sin(modelYaw_);
174 Mat4 r = Mat4::identity();
175 r.m[0] =
c; r.m[2] =
s;
176 r.m[8] = -
s; r.m[10] =
c;
177 for (
int i = 0; i < 16; ++i) uniforms_.
model[i] = r.m[i];
180 for (
auto &
c : asset_.clusters)
c.errorRScreen =
c.errorR * uniforms_.params[2];
185 int clusters =
static_cast<int>(asset_.
clusters.size());
186 if (clusters <= 0 || !backend_.
state)
return 0;
187 lastVisible_ =
vgUpdate(backend_, clusters, visibleCapacity_, width_, height_);
192 std::vector<std::uint32_t> pix;
196 const int n = outW * outH;
197 for (
int i = 0; i <
n; ++i) {
198 std::uint32_t packed = pix[i];
199 std::uint32_t
depth = packed >> 16;
200 std::uint32_t cid = packed & 0xFFFFu;
201 if (cid == 0
u &&
depth == 0xFFFFu) {
202 outRgba[4 * i + 0] = 4;
203 outRgba[4 * i + 1] = 6;
204 outRgba[4 * i + 2] = 12;
205 outRgba[4 * i + 3] = 255;
209 std::uint32_t
h = cid * 2654435761u;
210 float cr = (
h >> 16) & 0xFFu, cg = (
h >> 8) & 0xFFu, cb =
h & 0xFFu;
211 float mx = std::max({cr, cg, cb, 1.f});
212 float depthF =
static_cast<float>(
depth) / 65535.f;
213 float shade = 0.35f + 0.65f * (1.f - depthF);
214 outRgba[4 * i + 0] =
static_cast<unsigned char>(cr / mx * 255.f * shade);
215 outRgba[4 * i + 1] =
static_cast<unsigned char>(cg / mx * 255.f * shade);
216 outRgba[4 * i + 2] =
static_cast<unsigned char>(cb / mx * 255.f * shade);
217 outRgba[4 * i + 3] = 255;
225 if (!bd)
return nullptr;
226 if (!
resolve(
static_cast<unsigned char *
>(bd->getData()),
w,
h)) {
234 return static_cast<int>(asset_.
clusters.size());
242 if (clusterId < 0 ||
static_cast<std::size_t
>(clusterId) >= asset_.
clusters.size())
return -1;
243 return static_cast<int>(asset_.
clusters[clusterId].lodLevel);
248 for (
const auto &
c : asset_.
clusters)
m = std::max(
m,
static_cast<int>(
c.lodLevel));
In-memory byte buffer implementing eve::Data (ref-counted).
CPU preprocessor that builds a Nanite-style hierarchical cluster DAG from a triangle mesh:
bool build(const MeshInput &in, const Options &opt, VirtualGeometryAsset &out)
Returns false on empty/invalid input.
bool buildIcosphere(int subdiv)
Convenience: procedural unit icosphere, subdiv subdivision levels.
void setViewport(int width, int height, float fovYDeg, float errorPx=1.0f)
int update()
Run cull + raster; returns the number of visible clusters.
int getClusterCount() const
~VirtualGeometryRenderer()
void setModelYaw(float yaw)
Spin the virtualized model about Y by yaw radians each frame.
void setCameraSimple(float camX, float camY, float camZ, float nearZ=0.1f, float farZ=100.f)
Script-friendly: identity view (camera looking down -Z) + perspective.
void setCamera(const float view[16], const float proj[16], const float model[16], const float camPos[3])
int getMaxLodLevel() const
bool build(const float *positions, int vertexCount, const std::uint32_t *indices, int indexCount)
int getTotalTriangleCount() const
int getLodLevel(int clusterId) const
VirtualGeometryRenderer()
bool resolve(unsigned char *outRgba, int &outW, int &outH)
eve::data::ByteData * resolveByteData()
Resolve into a heap ByteData (RGBA) for scripting/display.
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
void vgReset(VgBackend &be, int visibleCapacity)
int vgUpdate(VgBackend &be, int clusterCount, int visibleCapacity, int viewW, int viewH)
void vgUploadUniforms(VgBackend &be, const VgUniforms &u)
bool vgReadPixels(VgBackend &be, std::vector< uint32_t > &out)
void vgCreate(VgBackend &be)
void vgDestroy(VgBackend &be)
void vgUpload(VgBackend &be, const VirtualGeometryAsset &asset)
std::vector< VgCluster > clusters
int totalTriangles() const