You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Irrlicht中添加过程化网格失败问题求助

Irrlicht多网格缓冲区地形生成访问错误问题

问题情况

在Irrlicht中生成过程化地形网格时,使用addOctreeSceneNode或addMeshSceneNode均失败,报错触发在irrArray.h的调试断点。GenerateMesh函数内所有检查都通过,Mesh的缓冲区数量、顶点/索引内容、材质纹理信息看起来都正常,但就是无法正常渲染。

如果移除分块缓冲区逻辑,只用一个忽略BlockType的缓冲区就能正常运行;但切换为IMesh会导致地形代码几乎全部重构,不想走这条路。

原问题代码

SMesh* Terrain::GenerateMesh(Chunk& chunk){

const vector<Chunk::Vertex>& vertices = chunk.vertices;
const vector<u16>& indices = chunk.indices;

SMeshBuffer* grassBuffer = new SMeshBuffer();
SMeshBuffer* dirtBuffer = new SMeshBuffer();
SMeshBuffer* rockBuffer = new SMeshBuffer();
SMeshBuffer* bedrockBuffer = new SMeshBuffer();


for (u16 i = 0; i < indices.size(); ++i) {
    u16 ix = indices[i];  // Get index from the indices vector

    // Check if the index is within the bounds of the vertices vector
    if (ix < vertices.size()) {
        // Access the vertex using the index
        Chunk::Vertex vertex = vertices[ix];

        // Handle the block types based on a more explicit comparison
        switch (vertex.type) {
        case Chunk::BLOCKTYPE::Grass:
            grassBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
            grassBuffer->Indices.push_back(ix);
            break;
        case Chunk::BLOCKTYPE::Dirt:
            dirtBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
            dirtBuffer->Indices.push_back(ix);
            break;
        case Chunk::BLOCKTYPE::Rock:
            rockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
            rockBuffer->Indices.push_back(ix);
            break;
        case Chunk::BLOCKTYPE::BedRock:
            bedrockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
            bedrockBuffer->Indices.push_back(ix);
            break;
        default:
            break;
        }
    }
    else {
        // Print additional debug information if the index is invalid
        printf("Invalid index: %d (Index exceeds vertices size of %zu)\n", ix, vertices.size());
    }
}

printf("GrassBuffer vertices: %i, indices: %i\n", grassBuffer->Vertices.size(), grassBuffer->Indices.size());
printf("DirtBuffer vertices: %i, indices: %i\n", dirtBuffer->Vertices.size(), dirtBuffer->Indices.size());
printf("RockBuffer vertices: %i, indices: %i\n", rockBuffer->Vertices.size(), rockBuffer->Indices.size());
printf("BedrockBuffer vertices: %i, indices: %i\n", bedrockBuffer->Vertices.size(), bedrockBuffer->Indices.size());

SMesh* mesh = new SMesh();

if(grassBuffer->Vertices.size() > 0)
{
    grassBuffer->Material.MaterialType = video::EMT_SOLID;
    grassBuffer->getMaterial().setTexture(0, grassTexture);
    mesh->addMeshBuffer(grassBuffer);
}
if(dirtBuffer->Vertices.size() > 0)
{
    dirtBuffer->Material.MaterialType = video::EMT_SOLID;
    dirtBuffer->getMaterial().setTexture(0, dirtTexture);
    mesh->addMeshBuffer(dirtBuffer);
}
if(rockBuffer->Vertices.size() > 0)
{
    rockBuffer->Material.MaterialType = video::EMT_SOLID;
    rockBuffer->getMaterial().setTexture(0, rockTexture);
    mesh->addMeshBuffer(rockBuffer);
}
if(bedrockBuffer->Vertices.size() > 0)
{
    bedrockBuffer->Material.MaterialType = video::EMT_SOLID;
    bedrockBuffer->getMaterial().setTexture(0, bedrockTexture);
    mesh->addMeshBuffer(bedrockBuffer);
}
mesh->recalculateBoundingBox();

grassBuffer->drop();
dirtBuffer->drop();
rockBuffer->drop();
bedrockBuffer->drop();

return mesh;
}

问题根源

核心错误是索引复用错误:每个子缓冲区(grassBuffer、dirtBuffer等)的Vertices是独立的局部列表,但你直接把原chunk的全局顶点索引塞进了缓冲区的Indices数组。比如原chunk的第10个顶点是Grass类型,被加到grassBuffer的Vertices里成为第0个顶点,但你却把10写入grassBuffer的Indices,渲染时会去访问grassBuffer->Vertices[10],直接越界触发irrArray的断言断点。

修复代码

SMesh* Terrain::GenerateMesh(Chunk& chunk){
    const vector<Chunk::Vertex>& vertices = chunk.vertices;
    const vector<u16>& indices = chunk.indices;

    SMeshBuffer* grassBuffer = new SMeshBuffer();
    SMeshBuffer* dirtBuffer = new SMeshBuffer();
    SMeshBuffer* rockBuffer = new SMeshBuffer();
    SMeshBuffer* bedrockBuffer = new SMeshBuffer();

    // 维护每个缓冲区的顶点映射:原全局索引 -> 局部索引
    unordered_map<u16, u16> grassVertexMap;
    unordered_map<u16, u16> dirtVertexMap;
    unordered_map<u16, u16> rockVertexMap;
    unordered_map<u16, u16> bedrockVertexMap;

    for (u16 i = 0; i < indices.size(); ++i) {
        u16 globalIx = indices[i];
        if (globalIx >= vertices.size()) {
            printf("Invalid index: %d (Index exceeds vertices size of %zu)\n", globalIx, vertices.size());
            continue;
        }

        const Chunk::Vertex& vertex = vertices[globalIx];
        u16 localIx;

        switch (vertex.type) {
        case Chunk::BLOCKTYPE::Grass:
            if (grassVertexMap.find(globalIx) == grassVertexMap.end()) {
                localIx = grassBuffer->Vertices.size();
                grassBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
                grassVertexMap[globalIx] = localIx;
            } else {
                localIx = grassVertexMap[globalIx];
            }
            grassBuffer->Indices.push_back(localIx);
            break;
        case Chunk::BLOCKTYPE::Dirt:
            if (dirtVertexMap.find(globalIx) == dirtVertexMap.end()) {
                localIx = dirtBuffer->Vertices.size();
                dirtBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
                dirtVertexMap[globalIx] = localIx;
            } else {
                localIx = dirtVertexMap[globalIx];
            }
            dirtBuffer->Indices.push_back(localIx);
            break;
        case Chunk::BLOCKTYPE::Rock:
            if (rockVertexMap.find(globalIx) == rockVertexMap.end()) {
                localIx = rockBuffer->Vertices.size();
                rockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
                rockVertexMap[globalIx] = localIx;
            } else {
                localIx = rockVertexMap[globalIx];
            }
            rockBuffer->Indices.push_back(localIx);
            break;
        case Chunk::BLOCKTYPE::BedRock:
            if (bedrockVertexMap.find(globalIx) == bedrockVertexMap.end()) {
                localIx = bedrockBuffer->Vertices.size();
                bedrockBuffer->Vertices.push_back(S3DVertex(vertex.position, vertex.normal, vertex.color, vertex.texCoords));
                bedrockVertexMap[globalIx] = localIx;
            } else {
                localIx = bedrockVertexMap[globalIx];
            }
            bedrockBuffer->Indices.push_back(localIx);
            break;
        default:
            break;
        }
    }

    printf("GrassBuffer vertices: %i, indices: %i\n", grassBuffer->Vertices.size(), grassBuffer->Indices.size());
    printf("DirtBuffer vertices: %i, indices: %i\n", dirtBuffer->Vertices.size(), dirtBuffer->Indices.size());
    printf("RockBuffer vertices: %i, indices: %i\n", rockBuffer->Vertices.size(), rockBuffer->Indices.size());
    printf("BedrockBuffer vertices: %i, indices: %i\n", bedrockBuffer->Vertices.size(), bedrockBuffer->Indices.size());

    SMesh* mesh = new SMesh();

    if(grassBuffer->Vertices.size() > 0)
    {
        grassBuffer->Material.MaterialType = video::EMT_SOLID;
        grassBuffer->getMaterial().setTexture(0, grassTexture);
        grassBuffer->recalculateBoundingBox(); // 给每个缓冲区单独计算包围盒
        mesh->addMeshBuffer(grassBuffer);
    }
    if(dirtBuffer->Vertices.size() > 0)
    {
        dirtBuffer->Material.MaterialType = video::EMT_SOLID;
        dirtBuffer->getMaterial().setTexture(0, dirtTexture);
        dirtBuffer->recalculateBoundingBox();
        mesh->addMeshBuffer(dirtBuffer);
    }
    if(rockBuffer->Vertices.size() > 0)
    {
        rockBuffer->Material.MaterialType = video::EMT_SOLID;
        rockBuffer->getMaterial().setTexture(0, rockTexture);
        rockBuffer->recalculateBoundingBox();
        mesh->addMeshBuffer(rockBuffer);
    }
    if(bedrockBuffer->Vertices.size() > 0)
    {
        bedrockBuffer->Material.MaterialType = video::EMT_SOLID;
        bedrockBuffer->getMaterial().setTexture(0, bedrockTexture);
        bedrockBuffer->recalculateBoundingBox();
        mesh->addMeshBuffer(bedrockBuffer);
    }
    mesh->recalculateBoundingBox();

    grassBuffer->drop();
    dirtBuffer->drop();
    rockBuffer->drop();
    bedrockBuffer->drop();

    return mesh;
}

额外优化点

  • 给每个子缓冲区单独调用recalculateBoundingBox(),确保每个缓冲区的包围盒正确,避免Octree节点计算错误
  • 使用unordered_map来映射全局索引到局部索引,避免重复添加相同顶点,减少冗余数据

内容的提问来源于stack exchange,提问作者bard mask

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:44:51