digitalmars.D.learn - Segmentation fault while reading a file
- Ruby The Roobster (369/369) Jul 31 I decided to attempt to write my own OBJ loading library, seeing
- matheus (36/39) Jul 31 I don't have I compiler in hand to try your code at moment, but
- Ruby The Roobster (3/3) Jul 31 Nevermind. The segfault happened because I accidentally used the
- IchorDev (7/7) Aug 01 Hey just a heads up, you might wanna use
- Ruby The Roobster (8/13) Aug 01 Thank you. I am not very well acquainted with the standard
- IchorDev (10/17) Aug 01 I’m actually not sure. I think the best stuff is usually the
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
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
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
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
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
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