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

tinyobj_parse_obj fails to read .mtl file #40

Open
hk-dunky opened this issue Jun 6, 2020 · 4 comments
Open

tinyobj_parse_obj fails to read .mtl file #40

hk-dunky opened this issue Jun 6, 2020 · 4 comments

Comments

@hk-dunky
Copy link

hk-dunky commented Jun 6, 2020

I attempted to load an OBJ file with tinyobj_parse_obj from gordon/gordon.obj. It contains an MTL file, gordon/gordon.mtl. The reading failed and I got the following output:

open: No such file or directory
TINYOBJ: Failed to parse material file 'gordon.mtl^M': -2

Not sure what the first line is. Maybe an error on my side.
I believe the ^M is a carriage return as it displays in Vim. I don't know why that's there as it's in the file name. I believe it also misses that gordon.mtl is in gordon/. I know there is a branch that fixes this.

@syoyo
Copy link
Owner

syoyo commented Jun 6, 2020

Probably your .obj contains mixture of line endings(CRLF and LF)

Please post a minimal reproducible .obj file and code.

@hk-dunky
Copy link
Author

hk-dunky commented Jun 6, 2020

main.c:

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "tinyobj_loader_c.h"

static char* mmap_file(size_t* len, const char* filename) {
#ifdef _WIN64
	HANDLE file =
		CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	assert(file != INVALID_HANDLE_VALUE);

	HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
	assert(fileMapping != INVALID_HANDLE_VALUE);

	LPVOID fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
	auto fileMapViewChar = (const char*)fileMapView;
	assert(fileMapView != NULL);
#else

	FILE* f;
	long file_size;
	struct stat sb;
	char* p;
	int fd;

	(*len) = 0;

	f = fopen(filename, "r");
	if (!f)
	{
		perror("open");
		return NULL;
	}
	fseek(f, 0, SEEK_END);
	file_size = ftell(f);
	fclose(f);

	fd = open(filename, O_RDONLY);
	if (fd == -1) {
		perror("open");
		return NULL;
	}

	if (fstat(fd, &sb) == -1) {
		perror("fstat");
		return NULL;
	}

	if (!S_ISREG(sb.st_mode)) {
		fprintf(stderr, "%s is not a file\n", "lineitem.tbl");
		return NULL;
	}

	p = (char*)mmap(0, (size_t)file_size, PROT_READ, MAP_SHARED, fd, 0);

	if (p == MAP_FAILED) {
		perror("mmap");
		return NULL;
	}

	if (close(fd) == -1) {
		perror("close");
		return NULL;
	}

	(*len) = (size_t)file_size;

	return p;

#endif
}

static void get_file_data(const char* filename, char** data, size_t* len) {
	const char* ext = strrchr(filename, '.');

	size_t data_len = 0;

	if (strcmp(ext, ".gz") == 0) {
		assert(0); /* todo */

	} else {
		*data = mmap_file(&data_len, filename);
	}

	(*len) = data_len;
}

int main(void)
{
	tinyobj_attrib_t attrib;
	tinyobj_shape_t *shapes;
	tinyobj_material_t *mats;
	size_t num_shapes, num_mats;

	tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &mats, &num_mats, "gordon/gordon.obj", get_file_data, TINYOBJ_FLAG_TRIANGULATE);

	tinyobj_attrib_free(&attrib);
	tinyobj_shapes_free(shapes, num_shapes);
	tinyobj_materials_free(mats, num_mats);

	return 0;
}

gordon/gordon.obj:

mtllib gordon.mtl

For gordon/gordon.mtl an empty file reproduces the issue.

@syoyo
Copy link
Owner

syoyo commented Jun 6, 2020

The issue is that simply sting gordon.mtl(from mtllib line) is passed to get_file_data callback.

You'll need to implement a file reader callback something like:

const char *obj_dir = ...;

static void get_file_data(const char* filename, char** data, size_t* len) {
  const char* ext = strrchr(filename, '.');

  size_t data_len = 0;

  if (strcmp(ext, ".gz") == 0) {
    assert(0); /* todo */
  } else if (strcmp(ext, ".mtl") == 0) {
    const char *filepath = concat_str(obj_dir, filename);
    *data = mmap_file(&data_len, filepath);
  } else {
    *data = mmap_file(&data_len, filename);
  }

  (*len) = data_len;
}

We are better to polish file callback function API(e.g. add userdata pointer to callback arguments, use dedicated callback API for .mtl file, etc).

@hk-dunky
Copy link
Author

hk-dunky commented Jun 6, 2020

Ah, thanks. I'll see if that fixes it.

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

No branches or pull requests

2 participants