Segmentation fault while reading a file
Ruby The Roobster
michaeleverestc79 at gmail.com
Wed Jul 31 23:06:30 UTC 2024
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.
More information about the Digitalmars-d-learn
mailing list