www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Segmentation fault while reading a file

reply Ruby The Roobster <michaeleverestc79 gmail.com> writes:
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.
Jul 31
parent reply matheus <matheus gmail.com> writes:
On Wednesday, 31 July 2024 at 23:06:30 UTC, Ruby The Roobster 
wrote:
 ... or is `readln` bugging out because the file (backpack.obj 
 contained in the linked .zip file) is too large?
 ...
I don't have I compiler in hand to try your code at moment, but about your concern over the size of the file, you could try a simple object[1]: g cube v 0.0 0.0 0.0 v 0.0 0.0 1.0 v 0.0 1.0 0.0 v 0.0 1.0 1.0 v 1.0 0.0 0.0 v 1.0 0.0 1.0 v 1.0 1.0 0.0 v 1.0 1.0 1.0 vn 0.0 0.0 1.0 vn 0.0 0.0 -1.0 vn 0.0 1.0 0.0 vn 0.0 -1.0 0.0 vn 1.0 0.0 0.0 vn -1.0 0.0 0.0 f 1//2 7//2 5//2 f 1//2 3//2 7//2 f 1//6 4//6 3//6 f 1//6 2//6 4//6 f 3//3 8//3 7//3 f 3//3 4//3 8//3 f 5//5 7//5 8//5 f 5//5 8//5 6//5 f 1//4 5//4 6//4 f 1//4 6//4 2//4 f 2//1 6//1 8//1 f 2//1 8//1 4//1 Matheus. [1] https://cs.wellesley.edu/~cs307/readings/obj-ojects.html
Jul 31
parent reply Ruby The Roobster <michaeleverestc79 gmail.com> writes:
Nevermind. The segfault happened because I accidentally used the 
Mesh class before loading OpenGL.  I don't know if it works as 
intended, but it no longer crashes.
Jul 31
parent reply IchorDev <zxinsworld gmail.com> writes:
Hey just a heads up, you might wanna use 
[`readText`](https://dlang.org/library/std/file/read_text.html) 
and 
[`lineSplitter`](https://dlang.org/library/std/string/line_splitter.html) just
so you don’t have to deal with file handles. Also you can use something like
[`formattedRead`](https://dlang.org/library/std/format/read/
ormatted_read.html) for parsing formatted floats more easily.
Also please only use `in` if you have `-preview=in` and you know 
what it’s for. You were probably looking for `const`, because 
`in` with `-preview=in` is not useful for strings.
Aug 01
parent reply Ruby The Roobster <michaeleverestc79 gmail.com> writes:
On Thursday, 1 August 2024 at 07:03:04 UTC, IchorDev wrote:
 Hey just a heads up, you might wanna use 
 [`readText`](https://dlang.org/library/std/file/read_text.html) 
 and 
 [`lineSplitter`](https://dlang.org/library/std/string/line_splitter.html) just
so you don’t have to deal with file handles.
 ...
Thank you. I am not very well acquainted with the standard library, and this cleans up things significantly. Question: Is there a good guide to Phobos anywhere? I would like to learn the more commonly used algorithms / convenience functions, so I don't have to look through the docs trying to find what I want, and so that I don't keep having to re-invent the wheel when trying to work with data.
Aug 01
parent IchorDev <zxinsworld gmail.com> writes:
On Thursday, 1 August 2024 at 14:42:36 UTC, Ruby The Roobster 
wrote:
 Thank you.  I am not very well acquainted with the standard 
 library, and this cleans up things significantly.

 Question:  Is there a good guide to Phobos anywhere?  I would 
 like to learn the more commonly used algorithms / convenience 
 functions, so I don't have to look through the docs trying to 
 find what I want, and so that I don't keep having to re-invent 
 the wheel when trying to work with data.
I’m actually not sure. I think the best stuff is usually the examples in the documentation. If you’re searching for whether something might be in Phobos, try seeing if there’s a module that sounds right in the [index](https://dlang.org/phobos/index.html). In general I just peruse different modules until I see a function that fits what I need. It also depends on how functional you like your code, there’s a lot of map/filter/etc. stuff that I regularly write my own ad-hoc code for.
Aug 01