Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

obj parsing doesn't know the file name, but mtllib is relative to that file #38

Open
martinhath opened this issue May 2, 2020 · 4 comments

Comments

@martinhath
Copy link

I have an obj and mtl file exported from blender in a directory, and I'd like to read them both.
I'm using tinyobj_parse_obj for this, which takes the .obj file as a char * as input. However, one of the commands in the file is mtllib <filename>.mtl, which it tries to read, and fails, since the files aren't in the same directory as the executable. Since tinyobj_parse_obj doesn't know anything about the location of the obj file it would be unable to locate the .mtl file, since I assume mtllib is supposed to give a relative path.

Is this a bug, or is there some way of handing this?

@syoyo
Copy link
Owner

syoyo commented May 2, 2020

It's just an unimplemented feature.

We need to implement a feature to supply search path to .mtl file or a callback function for mtllib to tinyobj_parse_obj API.

PR is welcome!

@iyenal
Copy link

iyenal commented May 8, 2020

@martinhath If you would like a solution to link object's materials to mtllib materials, I created a solution to instead to get the object name, get its material name, and use that material name to then look for its corresponding texture. Let me know if you would like me to upload it here :)

@martinhath
Copy link
Author

@syoyo Glad to hear it!

@iyenal I have a folder full of obj and corresponding mtl files, so I think the original route of finding the mtl from the location of the obj would be best for me. Thanks anyways!

I ended up hacking something together in which the user can supply the filename of the obj, extract the directory from that, and append the relative path onto it. Since I'll always supply the filename, I didn't bother (yet) having any error checks, so the code is likely brittle to anyone that doesn't have my exact use-case.

I appreciate the decoupling of file reading and .obj parsing that the current API has, and so I don't think my current solution is fit for integration in the library. This might be what @syoyo is suggesting, but I think a better solution is to have tinyobj_parrse_obj take a char *(*cb)(const char *rel, void *param) and a void *param, where cb would take the relative filename and a pointer to some user data (param, for local state) and return a char * to the full text, or something like this?

If anyone finds this useful, these are my changes:

457c457
<     const char *p = (const char *) memchr(s, 0, n);
---
>     const char *p = memchr(s, 0, n);
1208,1212d1207
< int tinyobj_parse_obj_with_filename(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
<                       size_t *num_shapes, tinyobj_material_t **materials_out,
<                       size_t *num_materials_out, const char *buf, size_t len,
<                       const char *filename, unsigned int flags);
< 
1217,1223d1211
<   return tinyobj_parse_obj_with_filename(attrib, shapes, num_shapes, materials_out, num_materials_out, buf, len, NULL, flags);
< }
< 
< int tinyobj_parse_obj_with_filename(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
<                       size_t *num_shapes, tinyobj_material_t **materials_out,
<                       size_t *num_materials_out, const char *buf, size_t len,
<                       const char *filename, unsigned int flags) {
1329c1317
<     char *relative_filename = my_strndup(commands[mtllib_line_index].mtllib_name,
---
>     char *filename = my_strndup(commands[mtllib_line_index].mtllib_name,
1331,1341d1318
<     char buffer[1024];
<     int filename_len = strlen(filename);
<     memcpy(buffer, filename, filename_len);
<     char *sentinel = buffer + filename_len;
<     while (buffer < sentinel && *sentinel != '/')
<         sentinel--;
<     if (*sentinel == '/')
<         sentinel++;
<     int rel_len = commands[mtllib_line_index].mtllib_name_len;
<     memcpy(sentinel, relative_filename, rel_len);
<     sentinel[rel_len] = 0;
1343c1320
<     int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, buffer, &material_table);
---
>     int ret = tinyobj_parse_and_index_mtl_file(&materials, &num_materials, filename, &material_table);
1347c1324
<       fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", buffer, ret);
---
>       fprintf(stderr, "TINYOBJ: Failed to parse material file '%s': %d\n", filename, ret);
1350c1327
<     TINYOBJ_FREE(relative_filename);
---
>     TINYOBJ_FREE(filename);
1381a1359,1372
>         /* @todo
>            if (commands[t][i].material_name &&
>            commands[t][i].material_name_len > 0) {
>            std::string material_name(commands[t][i].material_name,
>            commands[t][i].material_name_len);
> 
>            if (material_map.find(material_name) != material_map.end()) {
>            material_id = material_map[material_name];
>            } else {
>         // Assign invalid material ID
>         material_id = -1;
>         }
>         }
>         */

@syoyo
Copy link
Owner

syoyo commented May 9, 2020

We can implement both.

One is supply both filename(for the use of .mtl search) and its content, something like what libjsonnet's evaluateSnippet does:

https://github.com/google/jsonnet/blob/a6979e399ecd0e8d1091506201487a5c390d4d31/include/libjsonnet%2B%2B.h#L111

And another is supply callback functions for resource access(.obj, .mtl, etc)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants