I decided to attempt to write my own OBJ loading library, seeing as the spec for the format is readily available online. Building with DMD and on Windows, and testing on [this model](https://learnopengl.com/data/models/backpack.zip), the code acts normally for a while, and then apparently segfaults after trying to call readln().

The code is as follows:

```d
module mesh;

public import gl3n.linalg;
public import gl3n.math;
import shader;
import bindbc.opengl;
import std.conv : to;
import texture;

struct Vertex
{
    vec3 position;
    vec3 normal;
    vec2 texCoord;
}

struct Mat
{
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    vec3 emission;
    float alpha;
    float shiny;
    size_t[] diffuseTextures;
    size_t[] specularTextures;
    size_t[] ambientTextures; // won't be used in practice;
    size_t[] emissionTextures;
}

struct Texture
{
    uint id;
    string type;
}

class Mesh
{
    public:
        Vertex[] vertices;
        size_t[] indices;
        Texture[] textures;

        Mat[string] materials;

this(Vertex[] vertices, size_t[] indices, Texture[] textures)
        {
            this.vertices = vertices.dup;
            this.indices = indices.dup;
            this.textures = textures.dup;
            setupMesh();
        }

        this(string filename)
        {
            this.readFromFile(filename);
            setupMesh();
        }
        void Draw(Shader shader)
        {
            glBindVertexArray(VAO);
            uint diffuseNR, specNR = 1;
            for(uint i = 0; i < textures.length; i++)
            {
                glActiveTexture(GL_TEXTURE0 + i);
                string name = textures[i].type;
                if(name == "diffuse_texture")
                    name ~= to!string(++diffuseNR);
                else if(name == "spec_texture")
name = "material." ~ name ~ to!string(++specNR);
                shader.setUniform(name.dup, i);
                glBindTexture(GL_TEXTURE_2D, textures[i].id);
            }
            glActiveTexture(GL_TEXTURE0);

glDrawElements(GL_TRIANGLES, cast(int)indices.length, GL_UNSIGNED_INT, cast(void*)0);
            glBindVertexArray(0);
        }
    private:
        // render data
        uint VAO, VBO, EBO;
        vec3[] positions; //We ignore W for now
vec2[] texCoords; //We are currently working w/ 2D textures
        vec3[] normals;
        Face[] faces;
        size_t[size_t[3]] lookup_table;

        void setupMesh()
        {
            glGenBuffers(1, &VAO);
            glBindVertexArray(VAO);

            glGenBuffers(1, &VBO);
            glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.length * Vertex.sizeof, vertices.ptr, GL_STATIC_DRAW);

            glGenBuffers(1, &EBO);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof, indices.ptr, GL_STATIC_DRAW);

            // Memory Layout go brrrrrrrrrrrrrrrrrrrrrr
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * float.sizeof, cast(void*)0);
            glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * float.sizeof, cast(void*)(Vertex.normal.offsetof));
            glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * float.sizeof, cast(void*)(Vertex.texCoord.offsetof));
            glEnableVertexAttribArray(2);
        }

        void readFromFile(in string filename)
        {
            bool smooth = false;
            import std.stdio;
            import std.exception : ErrnoException;
            import std.uni;
            import std.conv : to;
            import std.array;

            import std.algorithm;
            File file;
            try
            {
                file = File(filename, "r");
            }
            catch(ErrnoException e)
            {
writeln("Failed to open file \'" ~ filename ~ "\': " ~ e.msg);
                return;
            }

            {
                scope(exit) file.close();
                char[] buf;
                while(file.readln(buf))
                {
                    if(buf.startsWith("v "))
                    {
                        vec3 vertex;
                        size_t i = 2;
                        string temp;
                        static foreach (x; ["x", "y", "z"])
                        {
for(; i < buf.length && !buf[i].isNumber; i++) {} for(; i < buf.length && (buf[i].isNumber || buf[i] == '.'); i++) { temp ~= buf[i]; } mixin("vertex." ~ x ~ " = to!float(temp);");
                            temp = "";
                        }

                        positions ~= vertex;
                    }
                    else if(buf.startsWith("vn"))
                    {
                        vec3 normal;
                        size_t i = 2;
                        string temp;
                        static foreach (x; ["x", "y", "z"])
                        {
for(;i < buf.length && !buf[i].isNumber; i++) {} for(; i < buf.length && (buf[i].isNumber || buf[i] == '.'); i++) { temp ~= buf[i]; } mixin("normal." ~ x ~ " = to!float(temp);");
                            temp = "";
                        }
                        normals ~= normal;
                    }
                    else if(buf.startsWith("vt"))
                    {
                        vec2 tex;
                        size_t i = 2;
                        string temp;
                        static foreach (x; ["x", "y"])
                        {
for(;i < buf.length && !buf[i].isNumber; i++) {} for(; i < buf.length && (buf[i].isNumber || buf[i] == '.'); i++) { temp ~= buf[i]; } mixin("tex." ~ x ~ " = to!float(temp);");
                            temp = "";
                        }
                        texCoords ~= tex;
                    }
                    else if(buf.startsWith("f ")) // Handle Faces
                    {
                        Face face;
                        Vertex vertex;
                        size_t i = 2;
                        string temp;
while(i < buf.length-1) // -1 because otherwise the loop runs an extra time and `to` fails to convert a blank string into a `size_t`
                        {
                            face.vertices.length++;
                            uint j = 0;
static foreach (x; ["positions", "texCoords", "normals"])
                            {
for(; i < buf.length && !buf[i].isNumber; i++) {}
                                "1".writeln;
for(; i < buf.length && buf[i].isNumber; i++) { temp ~= buf[i]; }
                                "2".writeln;
mixin("vertex." ~ x[0 .. $-1] ~ " = " ~ x ~ "[to!size_t(temp) - 1];");
                                "3".writeln;
face.vertices[$-1][j] = to!size_t(temp) - 1;
                                "4".writeln;
                                ++j;
                                temp = "";
                            }
                            vertex = vertex.init;
                        }
                        face.smooth = smooth;
                        faces ~= face;
                        "END".writeln;
                    }
else if(buf.startsWith("s")) // smoothing group
                    {
                        uint i = 1;
for(; i < buf.length && !buf[i].isAlphaNum; i++) {} if(buf[i] == '0' || (i + 2 < buf.length && buf[i .. i + 3] == "off"))
                            smooth = false;
                        else if(buf[i].isNumber)
                            smooth = true;
                        else
throw new Exception("Corrupted object file: " ~ filename);
                    }
else if(buf.startsWith("mtllib")) // Handle material data
                    {
                        foreach(fil; buf[6 .. $].split!isWhite)
                        {
                            if(fil.length == 0)
                                continue;
                            else
                            {

                                File mtl;
                                try
                                {
                                    mtl = File(filename, "r");
                                }
                                catch (ErrnoException e)
                                {
writeln("Could not open MTL file \'" ~ filename ~ "\': " ~ e.msg);
                                    return;
                                }
                                {
                                    scope(exit) mtl.close();
                                    char[] mtlbuf;
                                    string key;
Loop: while(mtl.readln(mtlbuf))
                                    {
if(mtlbuf.startsWith("newmtl"))
                                        {
key = join(mtlbuf[6 .. $].split!isWhite).idup; // Name of the material
                                            "here".writeln;
                                        }
static foreach(vec; ["ambient", "diffuse", "specular", "emission"])
                                        {
if(mtlbuf.startsWith("K" ~ vec[0])) // Ambient Color
                                            {
                                                ubyte j = 0;
foreach(val; mtlbuf[2 .. $].split!isWhite)
                                                {
final switch(j)
                                                    {
                                                        case 0:
mixin("materials[key]." ~ vec ~ ".x = to!float(val);");
                                                            break;
                                                        case 1:
mixin("materials[key]." ~ vec ~ ".y = to!float(val);");
                                                            break;
                                                        case 2:
mixin("materials[key]." ~ vec ~ ".z = to!float(val);");
                                                            break;
                                                    }
                                                    ++j;
                                                }
                                                continue Loop;
                                            }
if(mtlbuf.startsWith("map_K" ~ vec[0])) //Texture Maps
                                            {
string fname = mtlbuf[5 .. $].split!isWhite.join.idup; int width, hight, nrChannels; newTexture(fname, width, hight, nrChannels); mixin("materials[key]." ~ vec ~ "Textures ~= textures.length - 1;");
                                            }
                                        }
if(mtlbuf.startsWith("d")) //Alpha
                                        {
materials[key].alpha = mtlbuf[1 .. $].split!isWhite.join.to!float;
                                        }
else if(mtlbuf.startsWith("Tr"))
                                        {
materials[key].alpha = 1 - mtlbuf[1 .. $].split!isWhite.join.to!float;
                                        }
else if(mtlbuf.startsWith("Ns")) // Shininess
                                        {
materials[key].shiny = mtlbuf[2 .. $].split!isWhite.join.to!float;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

// Process the face & Vertex data to create a vertex array and an EBO.
            foreach(face; faces)
            {
                foreach(v; face.vertices)
                {
vertices ~= Vertex(positions[v[0]], normals[v[2]], texCoords[v[1]]);
                    lookup_table[v] = vertices.length - 1;
                    indices ~= lookup_table[v];
                }
            }
        }
}

struct Face
{
    size_t[3][] vertices;
    bool smooth = false;
}
```

The various `writeln` statements were my attempt to find the cause of the SEGFAULT, and it apparently occurs at some point while executing `readln()` as part of the condition of the while loop. Am I doing something wrong that is leading to memory being illegally accessed, or is `readln` bugging out because the file (backpack.obj contained in the linked .zip file) is too large?

Thanks in advance.

Reply via email to