Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ Viewer of 3D models in ASCII, written in C.

![Example usage capture.](capture.gif)

**Note**: Currently, the program only supports [Wavefront .obj](https://en.wikipedia.org/wiki/Wavefront_.obj_file) files.
Supported formats:

* [Wavefront .obj](https://en.wikipedia.org/wiki/Wavefront_.obj_file).
* [ASCII Polygon File Format .ply](https://en.wikipedia.org/wiki/PLY_(file_format)) (partially).

## Compile an run the program

Expand Down
145 changes: 145 additions & 0 deletions src/model.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,3 +454,148 @@ struct model *model_load_from_obj(const char *fname, bool color_support)
model_validate_idxs(model);
return model;
}

struct model *model_load_from_ply(const char *fname)
{
FILE *fp = fopen(fname, "r");
if (!fp)
{
fprintf(stderr, "ERROR: failed to load file \"%s\".\n", fname);
return NULL;
}

// Create a new model
struct model *model = model_init();

// Read each line of the file
char buffer[256];

int vertex_count = 0;
int face_count = 0;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that all PLY files start with a ply line. I think we should check for that and throw an exit(1) printing "invalid format" to stderr if it is missing.

Also, I read that there are both a binary PLY format and a text PLY format. How can we check that this is indeed a text PLY file?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is still pending, but we should check for a line with

format ascii 1.0

in the header.

while (fgets(buffer, sizeof(buffer), fp))
{
char *p = buffer;
while (*p)
{
if (*p == '\n' || *p == '\r')
*p = '\0';
if (*p == '\t')
*p = ' ';
p++;
}

char *bufferp = buffer;
char *instr = str_chop_skip_empty(&bufferp, " ");

if (!instr)
continue;

if (strcmp(instr, "element") == 0)
{
char *type = str_chop_skip_empty(&bufferp, " ");
int count = 0;
parse_int(&bufferp, &count);

if (strcmp(type, "vertex") == 0)
{
vertex_count = count;
}
else if (strcmp(type, "face") == 0)
{
face_count = count;
}
}
else if (strcmp(instr, "property") == 0)
{
// Skip property declarations
}
else if (strcmp(instr, "end_header") == 0)
{
// End of header, start reading data
break;
}
}

for (int i = 0; i < vertex_count; i++)
{
float f1, f2, f3;

if (!fgets(buffer, sizeof(buffer), fp))
{
fprintf(stderr, "ERROR: could not read vertex data.\n");
fclose(fp);
model_free(model);
return NULL;
}
char *bufferp = buffer;

if (!parse_float(&bufferp, &f1) || !parse_float(&bufferp, &f2) || !parse_float(&bufferp, &f3))
{
fprintf(stderr, "ERROR: unexpected vertex data format.\n");
fclose(fp);
model_free(model);
return NULL;
}

vec3 vec;
vec.x = f1;
vec.y = f2;
vec.z = f3;
Comment on lines +542 to +544
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the models appear rotated so probably this is not the correct ordering for the axis. Perhaps vec.y should be f3 and vec.z should be f2 ?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the ordering of the axes came inconsistent between the models PLY models. So the teapot.ply and the airplane.py had the wrong orientation.

Perhaps we should stick to the axes ordering used by blender when exporting .PLY files.


model_add_vertex(model, vec);
}

for (int i = 0; i < face_count; i++)
{
int list_length;
int i1, i2, i3;

if (!fgets(buffer, sizeof(buffer), fp))
{
fprintf(stderr, "ERROR: could not read face data.\n");
fclose(fp);
model_free(model);
return NULL;
}
char *bufferp = buffer;

if (!parse_int(&bufferp, &list_length))
{
fprintf(stderr, "ERROR: unexpected face data format.\n");
fclose(fp);
model_free(model);
return NULL;
}

if (list_length >= 3)
{
if (!parse_int(&bufferp, &i1) || !parse_int(&bufferp, &i2))
{
fprintf(stderr, "ERROR: unexpected face data format.\n");
fclose(fp);
model_free(model);
return NULL;
}
}

for (int k = 2; k < list_length; ++k)
{
if (!parse_int(&bufferp, &i3))
{
fprintf(stderr, "ERROR: unexpected face data format.\n");
fclose(fp);
model_free(model);
return NULL;
}

model_add_face(model, i1, i2, i3, -1);
// Shift for possible new triangular face
i2 = i3;
}
}
fclose(fp);

model_validate_idxs(model);
return model;
}
1 change: 1 addition & 0 deletions src/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct model
};

struct model *model_load_from_obj(const char *fname, bool color_support);
struct model *model_load_from_ply(const char *fname);

void model_invert_triangles(struct model *model);

Expand Down
42 changes: 39 additions & 3 deletions src/viewer.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,11 +493,47 @@ int main(int argc, char *argv[])

parse_arguments(argc, argv, &args);

struct model *model;
struct model *model = NULL;
char *file_ext = strrchr(args.input_file, '.');

if (!(model = model_load_from_obj(args.input_file, args.color_support)))
if (file_ext)
{
if (strcasecmp(file_ext, ".obj") == 0)
{
model = model_load_from_obj(args.input_file, args.color_support);
}
else if (strcasecmp(file_ext, ".ply") == 0)
{
model = model_load_from_ply(args.input_file);

if (args.color_support)
{
fprintf(stderr, "WARN: Colors for PLY format not supported.\n");
}
}
else if (strcasecmp(file_ext, ".mtl") == 0)
{
fprintf(stderr, "ERROR: The companion MTL file should not be passed as argument.\n");
fprintf(stderr, " It must be referenced inside the OBJ.\n");
exit(1);
}
else
{
fprintf(stderr, "ERROR: Unrecognized file extension \"%s\".\n", file_ext);
exit(1);
}
}
else
{
fprintf(stderr, "ERROR: Expected an extension on input file \"%s\".\n", args.input_file);
exit(1);
}

if (!model)
return 1;
model_invert_z(model); // Required by the OBJ format.

if (strcasecmp(file_ext, ".obj") == 0)
model_invert_z(model);

if (model->vertex_count == 0)
{
Expand Down