GithubHelp home page GithubHelp logo

fast_obj's People

Contributors

aspurdy avatar aurl avatar beastle9end avatar cadenji avatar davidkorczynski avatar hburd avatar kuranes avatar laurelkeys avatar maxrigout avatar sergof avatar thisistherk avatar zeux avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fast_obj's Issues

Allow certain vertex attributes to be skipped

Would be nice to e.g. ignore texture coordinates if you're not bothered about them. Float parsing takes the majority of execution time, so skipping parsing things that aren't needed would speed things up.

Probably requires a new API - fast_obj_read_with_options or something - to supply flags indicating which attributes are required.

Maybe an option to not skip empty objects?

I used this library (thank you for sharing it!) with ttf2mesh and it exports spaces as empty objects. The names still hold data about the characters but your library skips them because they have no geometry. Maybe add an option to not do that :).

Hacked my way out of it like so:

void flush_object(fastObjData* data)
{
    /* Add object if not empty */
    if (data->object.face_count > 0)
        array_push(data->mesh->objects, data->object);
    else if(data->object.name != NULL)
    {
        data->object.face_count=0;
        data->object.face_offset=0;
        data->object.index_offset=0;
        array_push(data->mesh->objects, data->object);
    }
    else
        object_clean(&data->object);
    
    /* Reset for more data */
    data->object = object_default();
    data->object.face_offset  = array_size(data->mesh->face_vertices);
    data->object.index_offset = array_size(data->mesh->indices);
}


Versionning in file header

Could next version have a version number in the header of fast_obj.h ?

like cgltf does here

Otherwise it's hard to know what version of fast_obj is used.

As it's a header only library, more often than not, it's directly added as a file in projects, so using commit date or commit log is not really very practical and guaranteed to succeed.

(would do a PR, but choosing the first version number and what kind of versioning is kind of personal taste thing)

Performance Ideas: 2 pass ?

Hi,

Wondering if you had tried or have experience around those 2 ideas:

  1. prepass for big allocation upfront and avoid realloc?
    (parse line numbers of v/n/vt then allocate once and for all )
  2. prepass for "simd token tricks" as in https://github.com/lemire/simdjson (blogpost https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/ paper https://arxiv.org/abs/1902.08318). Idea implemented for example in csv parsing https://github.com/geofflangdale/simdcsv

Infinite loop / crash with invalid input

Hi,
The following obj file causes the parse_face function to get stuck in an infinite loop:
f f
(3 letters only)

In the parse_face function, the pointer never gets advanced, because no valid integers could be read. Also, it will keep pushing new items into the array every iteration, so it will just run for a while, then it'll crash.

bug - off by one

The off by one bug is affecting vertex position, texcoords and normals count.
Another bug is fastobj report illum (in mtl file) to be 0 regardless of its value.

here is a sample cube (.obj) from blender, it has 8 vertices, 14 texcoords, and 6 normals:

mtllib untitled.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

and here is the mtl, notice the illum is 2:

newmtl Material
Ns 323.999994
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

finally the test program and its output:
the test:

#include <stdio.h>

#define FAST_OBJ_IMPLEMENTATION
#include "fast_obj.h"

int main(int argc, char **argv)
{
	static fastObjMesh *mesh;
	mesh = fast_obj_read("untitled.obj");
	if (mesh) {
		fprintf(stdout, "vertices count: %i\n", mesh->position_count);
		fprintf(stdout, "texture coords count: %i\n", mesh->texcoord_count);
		fprintf(stdout, "normals count: %i\n", mesh->normal_count);
		fprintf(stdout, "face count: %i\n", mesh->face_count);
		fprintf(stdout, "index count: %i\n", mesh->index_count);
		for (int i=0;i<mesh->index_count;++i)
			fprintf(stdout, "\tindices p:%i t:%i n:%i\n", mesh->indices[i].p, mesh->indices[i].t, mesh->indices[i].n);
		fprintf(stdout, "material count: %i\n", mesh->material_count);
		for (int i=0;i<mesh->material_count;++i) {
			fprintf(stdout, "\tambient: %f, %f, %f\n", mesh->materials[i].Ka[0], mesh->materials[i].Ka[1], mesh->materials[i].Ka[2]);
			fprintf(stdout, "\tdiffuse: %f, %f, %f\n", mesh->materials[i].Kd[0], mesh->materials[i].Kd[1], mesh->materials[i].Kd[2]);
			fprintf(stdout, "\tspecular: %f, %f, %f\n", mesh->materials[i].Ks[0], mesh->materials[i].Ks[1], mesh->materials[i].Ks[2]);
			fprintf(stdout, "\temission: %f, %f, %f\n", mesh->materials[i].Ke[0], mesh->materials[i].Ke[1], mesh->materials[i].Ke[2]);
			fprintf(stdout, "\ttransmittance: %f, %f, %f\n", mesh->materials[i].Kt[0], mesh->materials[i].Kt[1], mesh->materials[i].Kt[2]);
			fprintf(stdout, "\tshininess: %f\n", mesh->materials[i].Ns);
			fprintf(stdout, "\trefraction index: %f\n", mesh->materials[i].Ni);
			fprintf(stdout, "\ttransmission filter: %f, %f, %f\n", mesh->materials[i].Tf[0], mesh->materials[i].Tf[1], mesh->materials[i].Tf[2]);
			fprintf(stdout, "\tdisolve: %f\n", mesh->materials[i].d);
			fprintf(stdout, "\tillum: %i\n", mesh->materials[i].illum);
		}
		fprintf(stdout, "object count: %i\n", mesh->object_count);
		fprintf(stdout, "group count: %i\n", mesh->group_count);
		fast_obj_destroy(mesh);
	}
	return 0;
}
/*`

the output, notice vertex position/texcoords/normals count are off by one, and illum reported as 0:
vertices count: 9
texture coords count: 15
normals count: 7
face count: 12
index count: 36
	indices p:5 t:1 n:1
	indices p:3 t:2 n:1
	indices p:1 t:3 n:1
	indices p:3 t:2 n:2
	indices p:8 t:4 n:2
	indices p:4 t:5 n:2
	indices p:7 t:6 n:3
	indices p:6 t:7 n:3
	indices p:8 t:8 n:3
	indices p:2 t:9 n:4
	indices p:8 t:10 n:4
	indices p:6 t:11 n:4
	indices p:1 t:3 n:5
	indices p:4 t:5 n:5
	indices p:2 t:9 n:5
	indices p:5 t:12 n:6
	indices p:2 t:9 n:6
	indices p:6 t:7 n:6
	indices p:5 t:1 n:1
	indices p:7 t:13 n:1
	indices p:3 t:2 n:1
	indices p:3 t:2 n:2
	indices p:7 t:14 n:2
	indices p:8 t:4 n:2
	indices p:7 t:6 n:3
	indices p:5 t:12 n:3
	indices p:6 t:7 n:3
	indices p:2 t:9 n:4
	indices p:4 t:5 n:4
	indices p:8 t:10 n:4
	indices p:1 t:3 n:5
	indices p:3 t:2 n:5
	indices p:4 t:5 n:5
	indices p:5 t:12 n:6
	indices p:1 t:3 n:6
	indices p:2 t:9 n:6
material count: 1
	ambient: 1.000000, 1.000000, 1.000000
	diffuse: 0.800000, 0.800000, 0.800000
	specular: 0.500000, 0.500000, 0.500000
	emission: 0.000000, 0.000000, 0.000000
	transmittance: 0.000000, 0.000000, 0.000000
	shininess: 324.000000
	refraction index: 1.450000
	transmission filter: 1.000000, 1.000000, 1.000000
	disolve: 1.000000
	illum: 0
object count: 1
group count: 1*/

Potential Security Issue

Hello,

We recently received a vulnerability disclosure against your repository. I couldn't find an e-mail to contact or a security process to follow, so created this issue instead.

If you would like me to e-mail over the details or put them on the GitHub Issue, I'm more than happy to facilitate this for you. Otherwise, you can access the advisory here.

It is private to you and the discloser of the report.

If you have any questions, let me know.

-- Jamie from huntr.dev

Confused about OpenGL usage

I'm testing the library with a cube OBJ exported from Blender. It seems to load fine, but loading it into a VBO and EBO using this code doesn't work properly:

glGenVertexArrays(1, &mesh->VAO);
glBindVertexArray(mesh->VAO);

glGenBuffers(1, &mesh->VBO);
glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO);
glBufferData(GL_ARRAY_BUFFER, objmesh->position_count * sizeof(float) * 3, objmesh->positions, GL_STATIC_DRAW);

glGenBuffers(1, &mesh->EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, objmesh->index_count * sizeof(unsigned int), objmesh->indices, GL_STATIC_DRAW);

When drawing with GL_TRIANGLES, nothing is displayed but if I use GL_LINES they go out from the origin to the 8 vertices of the cube. I think it's an issue with the indices, but I've been trying for days and I can't seem to figure out why

image
(view from inside renderdoc)

Thanks!

cube.obj:

# Blender 4.0.1
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
s 0
f 5 3 1
f 3 8 4
f 7 6 8
f 2 8 6
f 1 4 2
f 5 2 6
f 5 7 3
f 3 7 8
f 7 5 6
f 2 4 8
f 1 3 4
f 5 1 2

obj writer

fast_obj works quite well as a reader. For application that do rad/write of OBJ, it would be nice to be able to be able to save an in-memory obj to disk as a pair of OBJ/MTL files. An example of this is the cgltf project.

Any plans to add this in the future?

Capital vs non-capital

else if (p[0] == 'b' &&

I've encountered in my .mtl the map_Bump ( note the capital letter ). This caused fast_obj not to get the path.
I propose this simple fix:

else if (( p[0] == 'b' || p[0] == 'B' )

Cheers !

Support both Unix and Windows style paths

I noticed that if I'm loading a model on Windows and I use Unix style paths, the library cannot find the .mtl file related to my .obj file, this happens because string_find_last only searches for one type of slash inside the original path in order to generate the base path.

I suggest the following edit at line 1338:

    /* Find base path for materials/textures */
    const char *sep, *sep2;
    int found_sep;

    sep = sep2 = 0;
    found_sep = string_find_last(path, FAST_OBJ_SEPARATOR, &sep);
    found_sep = found_sep || string_find_last(path, FAST_OBJ_OTHER_SEP, &sep2);
    if (sep2 > sep) sep = sep2;
    if (found_sep)
        data.base = string_substr(path, 0, sep + 1);

Possible memory leak when calling fast_obj_read_with_callbacks()

For example, in the following code snippet, lines 1398 and 1449 return the function without freeing the allocated memory. These problems all happen when there is not enough memory, so is this intentional?

I think this is a bug, we should make sure the temporary resources are properly released returning the function.

fast_obj/fast_obj.h

Lines 1389 to 1449 in d2c2732

/* Open file */
file = callbacks->file_open(path, user_data);
if (!file)
return 0;
/* Empty mesh */
m = (fastObjMesh*)(memory_realloc(0, sizeof(fastObjMesh)));
if (!m)
return 0;
m->positions = 0;
m->texcoords = 0;
m->normals = 0;
m->face_vertices = 0;
m->face_materials = 0;
m->indices = 0;
m->materials = 0;
m->objects = 0;
m->groups = 0;
/* Add dummy position/texcoord/normal */
array_push(m->positions, 0.0f);
array_push(m->positions, 0.0f);
array_push(m->positions, 0.0f);
array_push(m->texcoords, 0.0f);
array_push(m->texcoords, 0.0f);
array_push(m->normals, 0.0f);
array_push(m->normals, 0.0f);
array_push(m->normals, 1.0f);
/* Data needed during parsing */
data.mesh = m;
data.object = object_default();
data.group = group_default();
data.material = 0;
data.line = 1;
data.base = 0;
/* Find base path for materials/textures */
{
const char* sep1 = strrchr(path, FAST_OBJ_SEPARATOR);
const char* sep2 = strrchr(path, FAST_OBJ_OTHER_SEP);
/* Use the last separator in the path */
const char* sep = sep2 && (!sep1 || sep1 < sep2) ? sep2 : sep1;
if (sep)
data.base = string_substr(path, 0, sep - path + 1);
}
/* Create buffer for reading file */
buffer = (char*)(memory_realloc(0, 2 * BUFFER_SIZE * sizeof(char)));
if (!buffer)
return 0;

We ported FastObj to Unity

Just writing here that we used fast_obj in an Unity 3D package for faster Obj loading in Unity in the project I'm working in. If anyone is interested in that you can find it here: https://github.com/equinor/FastObjUnity

It should™ work at runtime in Unity, and is quite fast and nice for most common ".obj" files if you want to quickly preview or use them in a Unity context.

Thanks for a great, fast tool 👍

Feature request: new release

Hello and thank you for developing fast_obj.
There has been many improvements since the last release and the library is used in Organic Maps among other projects. Could you consider doing a release shortly (or a RC) please?

Thanks!

Would it make sense to unify materials/vertices/indices arrays?

I'm interested in making a PR that moves fastObjGroup::materials, fastObjGroup::vertices and fastObjGroup::indices to fastObjMesh (probably calling them face_materials, face_vertices and face_indices), and changing fastObjGroup to something along the lines of:

typedef struct
{
    /* Group name */
    char*                       name;

    /* Number of faces */
    unsigned int                face_count;

    /* Offset of face data in fastObjMesh::face_* arrays */
    size_t                           face_offset;

} fastObjGroup;

Would that make sense / is that PR likely to be merged? The reason I'd like to make this change is that it makes parsing .obj easier for clients that don't care about the group data - since group data is completely extraneous and doesn't always provide meaningful splitting (material definition is per-face, not per-group), it seems like in most cases you'd want to ignore it.

I'm looking at replacing a custom .obj file parser (because the world doesn't need another .obj loader :P) with this one in several places where group data is inessential and this is the last small usability barrier left.

Support for line continuations

OBJ allows line continuations: if you put a backslash at the end of a line, parsing is supposed to continue on the next line as if it were part of the same line (Source: https://www.fileformat.info/format/wavefrontobj/egff.htm). fast_obj doesn't handle this properly and can seg fault when parsing files with line continuations.

It's pretty rare to encounter this in the wild and handling it properly would likely have a performance impact so maybe it's not actually worth supporting, but I thought it was at least worth mentioning.

Either way, thanks for making fast_obj available - it's a really nice little library!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.