Skip to content

Commit c9f0444

Browse files
committed
Fix vector subscript out of range for models with partial textures
- Add bounds check in KDTreeSoup::weight() for invalid texture indices - Add bounds checks in TexAtlas::width/height for invalid tex/level - Add bounds checks in TexAtlas::read and TexPyramid::read - Fix remap vector size in extractNodeTex (n_boxes instead of mesh.vert.size) - Add defensive bounds checks throughout extractNodeTex This fixes a crash when building Nexus files from models where not all meshes have textures (e.g., SunglassesKhronos.glb with 5 meshes but only 1 texture). The crash occurred on Windows Debug builds where MSVC detects vector subscript out of range.
1 parent 580c42f commit c9f0444

4 files changed

Lines changed: 37 additions & 8 deletions

File tree

src/nxsbuild/kdtree.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,9 @@ void KDTreeSoup::pushTriangle(Triangle &t) {
365365
double KDTreeSoup::weight(Triangle &t) {
366366
if(textures.size() == 0)
367367
return 0;
368+
// Skip weight calculation for triangles without valid texture
369+
if(t.tex < 0 || t.tex >= (int)textures.size())
370+
return 0;
368371
Vertex &v0 = t.vertices[0];
369372
Vertex &v1 = t.vertices[1];
370373
Vertex &v2 = t.vertices[2];

src/nxsbuild/nexusbuilder.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ QImage NexusBuilder::extractNodeTex(TMesh &mesh, int level, float &error, float
261261

262262
//erase boxes assigned to no texture, and remap vertex_to_box
263263
int count = 0;
264-
std::vector<int> remap(mesh.vert.size(), -1);
264+
std::vector<int> remap(n_boxes, -1);
265265
for(int i = 0; i < n_boxes; i++) {
266266
if(box_texture[i] == -1)
267267
continue;
@@ -271,8 +271,12 @@ QImage NexusBuilder::extractNodeTex(TMesh &mesh, int level, float &error, float
271271
}
272272
boxes.resize(count);
273273
box_texture.resize(count);
274-
for(int &b: vertex_to_box)
275-
b = remap[b];
274+
for(int &b: vertex_to_box) {
275+
if(b >= 0 && b < n_boxes)
276+
b = remap[b];
277+
else
278+
b = -1;
279+
}
276280

277281
std::vector<vcg::Point2i> sizes(boxes.size());
278282
std::vector<vcg::Point2i> origins(boxes.size());
@@ -288,9 +292,11 @@ QImage NexusBuilder::extractNodeTex(TMesh &mesh, int level, float &error, float
288292
}
289293
}
290294
int tex = box_texture[b];
295+
if(tex < 0) continue;
291296

292297
float w = atlas.width(tex, level); //img->size().width();
293298
float h = atlas.height(tex, level); //img->size().height();
299+
if(w <= 0 || h <= 0) continue;
294300
float px = 1/(float)w;
295301
float py = 1/(float)h;
296302
box.Offset(vcg::Point2f(px, py));
@@ -358,16 +364,24 @@ QImage NexusBuilder::extractNodeTex(TMesh &mesh, int level, float &error, float
358364
auto &p = mesh.vert[i];
359365
auto &uv = p.T().P();
360366
int b = vertex_to_box[i];
361-
if(b == -1) {
367+
if(b == -1 || b >= (int)origins.size() || b >= (int)mapping.size() || b >= (int)box_texture.size()) {
362368
uv = vcg::Point2f(0.0f, 0.0f);
363369
continue;
364370
}
365371
vcg::Point2i &o = origins[b];
366372
vcg::Point2i m = mapping[b];
367373

368374
int tex = box_texture[b];
375+
if(tex < 0) {
376+
uv = vcg::Point2f(0.0f, 0.0f);
377+
continue;
378+
}
369379
float w = atlas.width(tex, level);
370380
float h = atlas.height(tex, level);
381+
if(w <= 0 || h <= 0) {
382+
uv = vcg::Point2f(0.0f, 0.0f);
383+
continue;
384+
}
371385
float px = 1/(float)w;
372386
float py = 1/(float)h;
373387

@@ -413,6 +427,7 @@ QImage NexusBuilder::extractNodeTex(TMesh &mesh, int level, float &error, float
413427
for(int i = 0; i < mesh.face.size(); i++) {
414428
auto &face = mesh.face[i];
415429
int b = vertex_to_box[face.V(0) - &(mesh.vert[0])];
430+
if(b < 0 || b >= (int)origins.size()) continue;
416431
vcg::Point2i &o = origins[b];
417432
vcg::Point2i m = mapping[b];
418433
auto V0 = face.V(0)->T().P();
@@ -435,11 +450,13 @@ QImage NexusBuilder::extractNodeTex(TMesh &mesh, int level, float &error, float
435450
color[0] = ((boxid % 16)*135)%127 + 127; */
436451

437452
int source = box_texture[i];
453+
if(source < 0) continue;
438454
vcg::Point2i &o = origins[i];
439455
vcg::Point2i &s = sizes[i];
440456

441457
QImage rect = atlas.read(source, level, QRect(o[0], o[1], s[0], s[1]));
442-
painter.drawImage(mapping[i][0], mapping[i][1], rect);
458+
if(i < (int)mapping.size())
459+
painter.drawImage(mapping[i][0], mapping[i][1], rect);
443460

444461
// painter.fillRect(mapping[i][0], mapping[i][1], s[0], s[1], QColor(color[0], color[1], color[2]));
445462
// boxid++;
@@ -600,7 +617,6 @@ void NexusBuilder::processBlock(KDTreeSoup *input, StreamSoup *output, uint bloc
600617
mesh_size = mesh1.serializedSize(header.signature);
601618

602619
} else {
603-
604620
input->lock(mesh, block);
605621
//we need to replicate vertices where textured seams occours
606622

src/nxsbuild/texpyramid.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ bool TexPyramid::init(int tex, TexAtlas *c, LoadTexture &file) {
200200
}
201201

202202
QImage TexPyramid::read(int level, QRect region) {
203+
if(level < 0 || level >= (int)levels.size()) return QImage();
203204
return levels[level].read(region);
204205
}
205206

@@ -237,6 +238,7 @@ bool TexAtlas::addTextures(std::vector<LoadTexture> &textures) {
237238
}
238239

239240
QImage TexAtlas::read(int tex, int level, QRect region) {
241+
if(tex < 0 || tex >= (int)pyramids.size()) return QImage();
240242
return pyramids[tex].read(level, region);
241243
}
242244

src/nxsbuild/texpyramid.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,16 @@ class TexAtlas {
9090
QImage read(int tex, int level, QRect region);
9191
void buildLevel(int level);
9292

93-
int width(int tex, int level) { return pyramids[tex].levels[level].width; }
94-
int height(int tex, int level) { return pyramids[tex].levels[level].height; }
93+
int width(int tex, int level) {
94+
if(tex < 0 || tex >= (int)pyramids.size()) return 1;
95+
if(level < 0 || level >= (int)pyramids[tex].levels.size()) return 1;
96+
return pyramids[tex].levels[level].width;
97+
}
98+
int height(int tex, int level) {
99+
if(tex < 0 || tex >= (int)pyramids.size()) return 1;
100+
if(level < 0 || level >= (int)pyramids[tex].levels.size()) return 1;
101+
return pyramids[tex].levels[level].height;
102+
}
95103
void flush(int level);
96104
void pruneCache();
97105
void addImg(Index index, QImage img);

0 commit comments

Comments
 (0)