Search code examples
c#openglopentkassimp

OpenGL OpenTK - Inconsistent Model loading with Assimp


I am trying to import a multiple mesh model(sponza) with Assimp into my OpenGL renderer. Although I have a working obj file of the scene, the file seems to not load properly. For simplicity I leave out all the textures, even though the UVs still get loaded, and just set the color to blue. This is what I end up with

One-mesh high polygon count objects like the Stanford Dragon get loaded just fine.

This is how I load the object

if (Path.ToLower().EndsWith(".x"))        
{
                _scene = _context.ImportFile(Path, PostProcessSteps.LimitBoneWeights
                    | PostProcessSteps.Triangulate
                    | PostProcessSteps.ValidateDataStructure
                    | PostProcessSteps.FlipWindingOrder
                    | PostProcessSteps.FixInFacingNormals
                );
}
        

else
{
        _scene = _context.ImportFile(Path, PostProcessSteps.LimitBoneWeights
            | PostProcessSteps.Triangulate
            | PostProcessSteps.ValidateDataStructure
        );
}

for (int i = 0; i < _scene.MeshCount; i++)
{
    for (int j = 0; j < _scene.Meshes[i].Vertices.Count; j++)
    {
        _verts.Add(_scene.Meshes[i].Vertices[j].X);
        _verts.Add(_scene.Meshes[i].Vertices[j].Y);
        _verts.Add(_scene.Meshes[i].Vertices[j].Z);


        _verts.Add(_scene.Meshes[i].HasTextureCoords(0) ? _scene.Meshes[i].TextureCoordinateChannels[0][j].X : 0f);
        _verts.Add(_scene.Meshes[i].HasTextureCoords(0) ? _scene.Meshes[i].TextureCoordinateChannels[0][j].Y : 0f);


        _verts.Add(_scene.Meshes[i].Normals[j].X * InvertNormal.X);
        _verts.Add(_scene.Meshes[i].Normals[j].Y * InvertNormal.Y);
        _verts.Add(_scene.Meshes[i].Normals[j].Z * InvertNormal.Z);

    }
}
float[] _data = _verts.ToArray();
GL.NamedBufferData(VBO, _verts.Count * sizeof(float), _data, BufferUsageHint.StaticDraw);

// Indecis
List<uint> _indecisList = new List<uint>();
for (int i = 0; i < _debugMeshes; i++)
{
    uint[] _tempArr = _scene.Meshes[i].GetUnsignedIndices();
    for (int j = 0; j < _tempArr.Length; j++)
    {
        _indecisList.Add(_tempArr[j]);
    }
}
uint[] _indecis = _indecisList.ToArray();
GL.NamedBufferData(IBO, _indecis.Length * sizeof(uint), _indecis, BufferUsageHint.StaticDraw);
GL.BindVertexArray(Renderer.Forward.OBJ4.VAO);        
{       
    _modelViewMatrix = Matrix4.CreateScale(0.01f) * Matrix4.CreateTranslation(30, 0, 10);
    GL.UniformMatrix4(21, false, ref _modelViewMatrix);

    ColorMode(1);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, Renderer.Forward.OBJ4.IBO);
    GL.DrawElements(PrimitiveType.Triangles, 786801, DrawElementsType.UnsignedInt, 0); _drawCalls++;
    ColorMode(0);
}


Solution

  • Your code is just combining all meshes into a single vertex array + element array. However, the element data for each mesh is a 0-based index of the vertex data in that particular mesh. Hence, everything after the very first mesh will use element data which references the wrong vertices.

    If you want to fix this, you must offset your element index data by the sum of all vertices of the meshes you added to the vertex array before the current mesh.

    However, your approach seems dubious. Usually, the objects are splitted into different meshes for a reason, like different textures and material properties. You can't draw these in a single draw call anyway (at least not naively). GL does offer functions like glDrawElementsBaseVertex for scenarios where you want draw different sub-meshes from one combined vertex array, and which internally does the said offsetting for you.

    Also note that by combining the meshes in the way you do, you're also ignoring the transformations from the scene hierarchy.