mirror of
https://github.com/thisistherk/fast_obj.git
synced 2025-04-09 15:07:00 +00:00
Compare commits
47 commits
Author | SHA1 | Date | |
---|---|---|---|
|
9884aa86b0 | ||
|
fce8123891 | ||
|
8145eb0a94 | ||
|
ddfe2e7f86 | ||
|
42629f7442 | ||
|
9e2c3677b6 | ||
|
0ba37c7ff9 | ||
|
12b04fc1ab | ||
|
724e09ff1e | ||
|
c8707104b8 | ||
|
e24d2c5533 | ||
|
bcc68cca4c | ||
|
b827219dbf | ||
|
36cb5a8d89 | ||
|
33e0ee7162 | ||
|
2bb0b1f650 | ||
|
491efbbb59 | ||
|
cd5be148c3 | ||
|
27803406d7 | ||
|
20a9db53a0 | ||
|
2c91cb2993 | ||
|
9037820f30 | ||
|
a511f60249 | ||
|
1a8060257a | ||
|
66f467de8f | ||
|
6f4e843882 | ||
|
85778da5fc | ||
|
3a1efbcc79 | ||
|
d2c273248a | ||
|
ed03b86e7f | ||
|
9f4b38eb1b | ||
|
49c810ae1c | ||
|
86d7dc6e43 | ||
|
92551724cf | ||
|
d97389aa2d | ||
|
fed0e5760f | ||
|
5d1a8529cf | ||
|
eba6f59206 | ||
|
e7a99e63a0 | ||
|
723e0e4775 | ||
|
597b5a8420 | ||
|
cf2d64dea3 | ||
|
ac312c9c6d | ||
|
a40b84252a | ||
|
0f90b6fa0f | ||
|
2c9e41aed3 | ||
|
76143f7ef2 |
9 changed files with 380 additions and 130 deletions
6
.clusterfuzzlite/Dockerfile
Normal file
6
.clusterfuzzlite/Dockerfile
Normal file
|
@ -0,0 +1,6 @@
|
|||
FROM gcr.io/oss-fuzz-base/base-builder
|
||||
RUN apt-get update && apt-get install -y make autoconf automake libtool
|
||||
|
||||
COPY . $SRC/fast_obj
|
||||
COPY .clusterfuzzlite/build.sh $SRC/build.sh
|
||||
WORKDIR $SRC/fast_obj
|
3
.clusterfuzzlite/README.md
Normal file
3
.clusterfuzzlite/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# ClusterFuzzLite set up
|
||||
|
||||
This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite).
|
5
.clusterfuzzlite/build.sh
Normal file
5
.clusterfuzzlite/build.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash -eu
|
||||
$CXX $CFLAGS $LIB_FUZZING_ENGINE \
|
||||
$SRC/fast_obj/.clusterfuzzlite/fast_obj_fuzzer.cpp \
|
||||
-o $OUT/fast_obj_fuzzer \
|
||||
-I$SRC/fast_obj
|
27
.clusterfuzzlite/fast_obj_fuzzer.cpp
Normal file
27
.clusterfuzzlite/fast_obj_fuzzer.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#define FAST_OBJ_IMPLEMENTATION
|
||||
#include "fast_obj.h"
|
||||
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
char filename[256];
|
||||
sprintf(filename, "/tmp/libfuzzer.%d", getpid());
|
||||
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp)
|
||||
return 0;
|
||||
fwrite(data, size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
fastObjMesh *m = fast_obj_read(filename);
|
||||
if (!m) {
|
||||
unlink(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fast_obj_destroy(m);
|
||||
unlink(filename);
|
||||
return 0;
|
||||
}
|
1
.clusterfuzzlite/project.yaml
Normal file
1
.clusterfuzzlite/project.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
language: c++
|
30
.github/workflows/cflite_pr.yml
vendored
Normal file
30
.github/workflows/cflite_pr.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
name: ClusterFuzzLite PR fuzzing
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
permissions: read-all
|
||||
jobs:
|
||||
PR:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer: [address]
|
||||
steps:
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
|
||||
with:
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
language: c++
|
||||
bad-build-check: false
|
||||
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||
id: run
|
||||
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fuzz-seconds: 100
|
||||
mode: 'code-change'
|
||||
report-unreproducible-crashes: false
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
|
@ -1,10 +1,18 @@
|
|||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.5...3.31)
|
||||
|
||||
PROJECT(fast_obj)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 11)
|
||||
OPTION(FAST_OBJ_BUILD_TEST "Build test application" OFF)
|
||||
|
||||
ADD_EXECUTABLE(test test/test.cpp)
|
||||
TARGET_INCLUDE_DIRECTORIES(test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
ADD_LIBRARY(fast_obj INTERFACE)
|
||||
TARGET_INCLUDE_DIRECTORIES(fast_obj INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
ADD_LIBRARY(fast_obj_lib STATIC fast_obj.c)
|
||||
TARGET_INCLUDE_DIRECTORIES(fast_obj_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
||||
IF(${FAST_OBJ_BUILD_TEST})
|
||||
ADD_EXECUTABLE(fast_obj_test test/test.cpp)
|
||||
TARGET_COMPILE_FEATURES(fast_obj_test PRIVATE cxx_std_11)
|
||||
TARGET_LINK_LIBRARIES(fast_obj_test PRIVATE fast_obj_lib)
|
||||
ENDIF()
|
||||
|
|
|
@ -21,4 +21,9 @@ the mesh arrays.
|
|||
A simple test app is provided to compare speed against [tinyobjloader](https://github.com/syoyo/tinyobjloader) and
|
||||
check output matches.
|
||||
|
||||
### Version 1.3
|
||||
|
||||
Version 1.3 makes a small change to the API. Textures are now stored in a separate array on the
|
||||
`fastObjMesh` structure, and are referenced by index from materials, instead of being referenced
|
||||
by the material directly.
|
||||
|
||||
|
|
417
fast_obj.h
417
fast_obj.h
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
* fast_obj
|
||||
*
|
||||
* Version 1.0
|
||||
* Version 1.3
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018-2020 Richard Knight
|
||||
* Copyright (c) 2018-2021 Richard Knight
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -31,9 +31,11 @@
|
|||
#define FAST_OBJ_HDR
|
||||
|
||||
#define FAST_OBJ_VERSION_MAJOR 1
|
||||
#define FAST_OBJ_VERSION_MINOR 0
|
||||
#define FAST_OBJ_VERSION_MINOR 3
|
||||
#define FAST_OBJ_VERSION ((FAST_OBJ_VERSION_MAJOR << 8) | FAST_OBJ_VERSION_MINOR)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -63,16 +65,19 @@ typedef struct
|
|||
float d; /* Disolve (alpha) */
|
||||
int illum; /* Illumination model */
|
||||
|
||||
/* Texture maps */
|
||||
fastObjTexture map_Ka;
|
||||
fastObjTexture map_Kd;
|
||||
fastObjTexture map_Ks;
|
||||
fastObjTexture map_Ke;
|
||||
fastObjTexture map_Kt;
|
||||
fastObjTexture map_Ns;
|
||||
fastObjTexture map_Ni;
|
||||
fastObjTexture map_d;
|
||||
fastObjTexture map_bump;
|
||||
/* Set for materials that don't come from the associated mtllib */
|
||||
int fallback;
|
||||
|
||||
/* Texture map indices in fastObjMesh textures array */
|
||||
unsigned int map_Ka;
|
||||
unsigned int map_Kd;
|
||||
unsigned int map_Ks;
|
||||
unsigned int map_Ke;
|
||||
unsigned int map_Kt;
|
||||
unsigned int map_Ns;
|
||||
unsigned int map_Ni;
|
||||
unsigned int map_d;
|
||||
unsigned int map_bump;
|
||||
|
||||
} fastObjMaterial;
|
||||
|
||||
|
@ -109,6 +114,10 @@ typedef struct
|
|||
} fastObjGroup;
|
||||
|
||||
|
||||
/* Note: a dummy zero-initialized value is added to the first index
|
||||
of the positions, texcoords, normals and textures arrays. Hence,
|
||||
valid indices into these arrays start from 1, with an index of 0
|
||||
indicating that the attribute is not present. */
|
||||
typedef struct
|
||||
{
|
||||
/* Vertex data */
|
||||
|
@ -121,29 +130,50 @@ typedef struct
|
|||
unsigned int normal_count;
|
||||
float* normals;
|
||||
|
||||
unsigned int color_count;
|
||||
float* colors;
|
||||
|
||||
/* Face data: one element for each face */
|
||||
unsigned int face_count;
|
||||
unsigned int* face_vertices;
|
||||
unsigned int* face_materials;
|
||||
|
||||
/* Index data: one element for each face vertex */
|
||||
unsigned int index_count;
|
||||
fastObjIndex* indices;
|
||||
|
||||
/* Materials */
|
||||
unsigned int material_count;
|
||||
fastObjMaterial* materials;
|
||||
|
||||
/* Mesh groups */
|
||||
/* Texture maps */
|
||||
unsigned int texture_count;
|
||||
fastObjTexture* textures;
|
||||
|
||||
/* Mesh objects ('o' tag in .obj file) */
|
||||
unsigned int object_count;
|
||||
fastObjGroup* objects;
|
||||
|
||||
/* Mesh groups ('g' tag in .obj file) */
|
||||
unsigned int group_count;
|
||||
fastObjGroup* groups;
|
||||
|
||||
} fastObjMesh;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void* (*file_open)(const char* path, void* user_data);
|
||||
void (*file_close)(void* file, void* user_data);
|
||||
size_t (*file_read)(void* file, void* dst, size_t bytes, void* user_data);
|
||||
unsigned long (*file_size)(void* file, void* user_data);
|
||||
} fastObjCallbacks;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
fastObjMesh* fast_obj_read(const char* path);
|
||||
fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data);
|
||||
void fast_obj_destroy(fastObjMesh* mesh);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -156,7 +186,6 @@ void fast_obj_destroy(fastObjMesh* mesh);
|
|||
#ifdef FAST_OBJ_IMPLEMENTATION
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef FAST_OBJ_REALLOC
|
||||
|
@ -187,7 +216,8 @@ typedef struct
|
|||
/* Final mesh */
|
||||
fastObjMesh* mesh;
|
||||
|
||||
/* Current group */
|
||||
/* Current object/group */
|
||||
fastObjGroup object;
|
||||
fastObjGroup group;
|
||||
|
||||
/* Current material index */
|
||||
|
@ -249,15 +279,14 @@ static void* array_realloc(void* ptr, fastObjUInt n, fastObjUInt b)
|
|||
fastObjUInt sz = array_size(ptr);
|
||||
fastObjUInt nsz = sz + n;
|
||||
fastObjUInt cap = array_capacity(ptr);
|
||||
fastObjUInt ncap = 3 * cap / 2;
|
||||
fastObjUInt ncap = cap + cap / 2;
|
||||
fastObjUInt* r;
|
||||
|
||||
|
||||
if (ncap < nsz)
|
||||
ncap = nsz;
|
||||
ncap = (ncap + 15) & ~15u;
|
||||
|
||||
r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, b * ncap + 2 * sizeof(fastObjUInt)));
|
||||
r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, (size_t)b * ncap + 2 * sizeof(fastObjUInt)));
|
||||
if (!r)
|
||||
return 0;
|
||||
|
||||
|
@ -269,16 +298,18 @@ static void* array_realloc(void* ptr, fastObjUInt n, fastObjUInt b)
|
|||
|
||||
|
||||
static
|
||||
void* file_open(const char* path)
|
||||
void* file_open(const char* path, void* user_data)
|
||||
{
|
||||
(void)(user_data);
|
||||
return fopen(path, "rb");
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void file_close(void* file)
|
||||
void file_close(void* file, void* user_data)
|
||||
{
|
||||
FILE* f;
|
||||
(void)(user_data);
|
||||
|
||||
f = (FILE*)(file);
|
||||
fclose(f);
|
||||
|
@ -286,9 +317,10 @@ void file_close(void* file)
|
|||
|
||||
|
||||
static
|
||||
size_t file_read(void* file, void* dst, size_t bytes)
|
||||
size_t file_read(void* file, void* dst, size_t bytes, void* user_data)
|
||||
{
|
||||
FILE* f;
|
||||
(void)(user_data);
|
||||
|
||||
f = (FILE*)(file);
|
||||
return fread(dst, 1, bytes, f);
|
||||
|
@ -296,12 +328,13 @@ size_t file_read(void* file, void* dst, size_t bytes)
|
|||
|
||||
|
||||
static
|
||||
unsigned long file_size(void* file)
|
||||
unsigned long file_size(void* file, void* user_data)
|
||||
{
|
||||
FILE* f;
|
||||
long p;
|
||||
long n;
|
||||
|
||||
(void)(user_data);
|
||||
|
||||
f = (FILE*)(file);
|
||||
|
||||
p = ftell(f);
|
||||
|
@ -373,27 +406,6 @@ int string_equal(const char* a, const char* s, const char* e)
|
|||
}
|
||||
|
||||
|
||||
static
|
||||
int string_find_last(const char* s, char c, size_t* p)
|
||||
{
|
||||
const char* e;
|
||||
|
||||
e = s + strlen(s);
|
||||
while (e > s)
|
||||
{
|
||||
e--;
|
||||
|
||||
if (*e == c)
|
||||
{
|
||||
*p = (size_t)(e - s);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void string_fix_separators(char* s)
|
||||
{
|
||||
|
@ -412,12 +424,6 @@ int is_whitespace(char c)
|
|||
return (c == ' ' || c == '\t' || c == '\r');
|
||||
}
|
||||
|
||||
static
|
||||
int is_end_of_name(char c)
|
||||
{
|
||||
return (c == '\t' || c == '\r' || c == '\n');
|
||||
}
|
||||
|
||||
static
|
||||
int is_newline(char c)
|
||||
{
|
||||
|
@ -439,6 +445,21 @@ int is_exponent(char c)
|
|||
}
|
||||
|
||||
|
||||
static
|
||||
const char* skip_name(const char* ptr)
|
||||
{
|
||||
const char* s = ptr;
|
||||
|
||||
while (!is_newline(*ptr))
|
||||
ptr++;
|
||||
|
||||
while (ptr > s && is_whitespace(*(ptr - 1)))
|
||||
ptr--;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
const char* skip_whitespace(const char* ptr)
|
||||
{
|
||||
|
@ -459,6 +480,44 @@ const char* skip_line(const char* ptr)
|
|||
}
|
||||
|
||||
|
||||
static
|
||||
fastObjGroup object_default(void)
|
||||
{
|
||||
fastObjGroup object;
|
||||
|
||||
object.name = 0;
|
||||
object.face_count = 0;
|
||||
object.face_offset = 0;
|
||||
object.index_offset = 0;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void object_clean(fastObjGroup* object)
|
||||
{
|
||||
memory_dealloc(object->name);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void flush_object(fastObjData* data)
|
||||
{
|
||||
/* Add object if not empty */
|
||||
if (data->object.face_count > 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
fastObjGroup group_default(void)
|
||||
{
|
||||
|
@ -481,7 +540,7 @@ void group_clean(fastObjGroup* group)
|
|||
|
||||
|
||||
static
|
||||
void flush_output(fastObjData* data)
|
||||
void flush_group(fastObjData* data)
|
||||
{
|
||||
/* Add group if not empty */
|
||||
if (data->group.face_count > 0)
|
||||
|
@ -491,7 +550,7 @@ void flush_output(fastObjData* data)
|
|||
|
||||
/* Reset for more data */
|
||||
data->group = group_default();
|
||||
data->group.face_offset = array_size(data->mesh->face_vertices);
|
||||
data->group.face_offset = array_size(data->mesh->face_vertices);
|
||||
data->group.index_offset = array_size(data->mesh->indices);
|
||||
}
|
||||
|
||||
|
@ -530,7 +589,7 @@ const char* parse_float(const char* ptr, float* val)
|
|||
double num;
|
||||
double fra;
|
||||
double div;
|
||||
int eval;
|
||||
unsigned int eval;
|
||||
const double* powers;
|
||||
|
||||
|
||||
|
@ -619,6 +678,23 @@ const char* parse_vertex(fastObjData* data, const char* ptr)
|
|||
array_push(data->mesh->positions, v);
|
||||
}
|
||||
|
||||
|
||||
ptr = skip_whitespace(ptr);
|
||||
if (!is_newline(*ptr))
|
||||
{
|
||||
/* Fill the colors array until it matches the size of the positions array */
|
||||
for (ii = array_size(data->mesh->colors); ii < array_size(data->mesh->positions) - 3; ++ii)
|
||||
{
|
||||
array_push(data->mesh->colors, 1.0f);
|
||||
}
|
||||
|
||||
for (ii = 0; ii < 3; ++ii)
|
||||
{
|
||||
ptr = parse_float(ptr, &v);
|
||||
array_push(data->mesh->colors, v);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
@ -692,8 +768,10 @@ const char* parse_face(fastObjData* data, const char* ptr)
|
|||
|
||||
if (v < 0)
|
||||
vn.p = (array_size(data->mesh->positions) / 3) - (fastObjUInt)(-v);
|
||||
else
|
||||
else if (v > 0)
|
||||
vn.p = (fastObjUInt)(v);
|
||||
else
|
||||
return ptr; /* Skip lines with no valid vertex index */
|
||||
|
||||
if (t < 0)
|
||||
vn.t = (array_size(data->mesh->texcoords) / 2) - (fastObjUInt)(-t);
|
||||
|
@ -719,6 +797,27 @@ const char* parse_face(fastObjData* data, const char* ptr)
|
|||
array_push(data->mesh->face_materials, data->material);
|
||||
|
||||
data->group.face_count++;
|
||||
data->object.face_count++;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
const char* parse_object(fastObjData* data, const char* ptr)
|
||||
{
|
||||
const char* s;
|
||||
const char* e;
|
||||
|
||||
|
||||
ptr = skip_whitespace(ptr);
|
||||
|
||||
s = ptr;
|
||||
ptr = skip_name(ptr);
|
||||
e = ptr;
|
||||
|
||||
flush_object(data);
|
||||
data->object.name = string_copy(s, e);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
@ -734,12 +833,10 @@ const char* parse_group(fastObjData* data, const char* ptr)
|
|||
ptr = skip_whitespace(ptr);
|
||||
|
||||
s = ptr;
|
||||
while (!is_end_of_name(*ptr))
|
||||
ptr++;
|
||||
|
||||
ptr = skip_name(ptr);
|
||||
e = ptr;
|
||||
|
||||
flush_output(data);
|
||||
flush_group(data);
|
||||
data->group.name = string_copy(s, e);
|
||||
|
||||
return ptr;
|
||||
|
@ -788,15 +885,17 @@ fastObjMaterial mtl_default(void)
|
|||
mtl.d = 1.0;
|
||||
mtl.illum = 1;
|
||||
|
||||
mtl.map_Ka = map_default();
|
||||
mtl.map_Kd = map_default();
|
||||
mtl.map_Ks = map_default();
|
||||
mtl.map_Ke = map_default();
|
||||
mtl.map_Kt = map_default();
|
||||
mtl.map_Ns = map_default();
|
||||
mtl.map_Ni = map_default();
|
||||
mtl.map_d = map_default();
|
||||
mtl.map_bump = map_default();
|
||||
mtl.fallback = 0;
|
||||
|
||||
mtl.map_Ka = 0;
|
||||
mtl.map_Kd = 0;
|
||||
mtl.map_Ks = 0;
|
||||
mtl.map_Ke = 0;
|
||||
mtl.map_Kt = 0;
|
||||
mtl.map_Ns = 0;
|
||||
mtl.map_Ni = 0;
|
||||
mtl.map_d = 0;
|
||||
mtl.map_bump = 0;
|
||||
|
||||
return mtl;
|
||||
}
|
||||
|
@ -815,17 +914,9 @@ const char* parse_usemtl(fastObjData* data, const char* ptr)
|
|||
|
||||
/* Parse the material name */
|
||||
s = ptr;
|
||||
while (!is_end_of_name(*ptr))
|
||||
ptr++;
|
||||
|
||||
ptr = skip_name(ptr);
|
||||
e = ptr;
|
||||
|
||||
|
||||
/* If there are no materials yet, add a dummy invalid material at index 0 */
|
||||
if (array_empty(data->mesh->materials))
|
||||
array_push(data->mesh->materials, mtl_default());
|
||||
|
||||
|
||||
/* Find an existing material with the same name */
|
||||
idx = 0;
|
||||
while (idx < array_size(data->mesh->materials))
|
||||
|
@ -837,8 +928,15 @@ const char* parse_usemtl(fastObjData* data, const char* ptr)
|
|||
idx++;
|
||||
}
|
||||
|
||||
/* If doesn't exist, create a default one with this name
|
||||
Note: this case happens when OBJ doesn't have its MTL */
|
||||
if (idx == array_size(data->mesh->materials))
|
||||
idx = 0;
|
||||
{
|
||||
fastObjMaterial new_mtl = mtl_default();
|
||||
new_mtl.name = string_copy(s, e);
|
||||
new_mtl.fallback = 1;
|
||||
array_push(data->mesh->materials, new_mtl);
|
||||
}
|
||||
|
||||
data->material = idx;
|
||||
|
||||
|
@ -857,16 +955,6 @@ void map_clean(fastObjTexture* map)
|
|||
static
|
||||
void mtl_clean(fastObjMaterial* mtl)
|
||||
{
|
||||
map_clean(&mtl->map_Ka);
|
||||
map_clean(&mtl->map_Kd);
|
||||
map_clean(&mtl->map_Ks);
|
||||
map_clean(&mtl->map_Ke);
|
||||
map_clean(&mtl->map_Kt);
|
||||
map_clean(&mtl->map_Ns);
|
||||
map_clean(&mtl->map_Ni);
|
||||
map_clean(&mtl->map_d);
|
||||
map_clean(&mtl->map_bump);
|
||||
|
||||
memory_dealloc(mtl->name);
|
||||
}
|
||||
|
||||
|
@ -897,12 +985,12 @@ const char* read_mtl_triple(const char* p, float v[3])
|
|||
|
||||
|
||||
static
|
||||
const char* read_map(fastObjData* data, const char* ptr, fastObjTexture* map)
|
||||
const char* read_map(fastObjData* data, const char* ptr, unsigned int* idx)
|
||||
{
|
||||
const char* s;
|
||||
const char* e;
|
||||
char* name;
|
||||
char* path;
|
||||
const char* s;
|
||||
const char* e;
|
||||
fastObjTexture* map;
|
||||
|
||||
|
||||
ptr = skip_whitespace(ptr);
|
||||
|
||||
|
@ -913,25 +1001,36 @@ const char* read_map(fastObjData* data, const char* ptr, fastObjTexture* map)
|
|||
|
||||
/* Read name */
|
||||
s = ptr;
|
||||
while (!is_end_of_name(*ptr))
|
||||
ptr++;
|
||||
|
||||
ptr = skip_name(ptr);
|
||||
e = ptr;
|
||||
|
||||
name = string_copy(s, e);
|
||||
/* Try to find an existing texture map with the same name */
|
||||
*idx = 1; /* skip dummy at index 0 */
|
||||
while (*idx < array_size(data->mesh->textures))
|
||||
{
|
||||
map = &data->mesh->textures[*idx];
|
||||
if (map->name && string_equal(map->name, s, e))
|
||||
break;
|
||||
|
||||
path = string_concat(data->base, s, e);
|
||||
string_fix_separators(path);
|
||||
(*idx)++;
|
||||
}
|
||||
|
||||
map->name = name;
|
||||
map->path = path;
|
||||
/* Add it to the texture array if it didn't already exist */
|
||||
if (*idx == array_size(data->mesh->textures))
|
||||
{
|
||||
fastObjTexture new_map = map_default();
|
||||
new_map.name = string_copy(s, e);
|
||||
new_map.path = string_concat(data->base, s, e);
|
||||
string_fix_separators(new_map.path);
|
||||
array_push(data->mesh->textures, new_map);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int read_mtllib(fastObjData* data, void* file)
|
||||
int read_mtllib(fastObjData* data, void* file, const fastObjCallbacks* callbacks, void* user_data)
|
||||
{
|
||||
unsigned long n;
|
||||
const char* s;
|
||||
|
@ -944,13 +1043,13 @@ int read_mtllib(fastObjData* data, void* file)
|
|||
|
||||
|
||||
/* Read entire file */
|
||||
n = file_size(file);
|
||||
n = callbacks->file_size(file, user_data);
|
||||
|
||||
contents = (char*)(memory_realloc(0, n + 1));
|
||||
if (!contents)
|
||||
return 0;
|
||||
|
||||
l = file_read(file, contents, n);
|
||||
l = callbacks->file_read(file, contents, n, user_data);
|
||||
contents[l] = '\n';
|
||||
|
||||
mtl = mtl_default();
|
||||
|
@ -989,8 +1088,7 @@ int read_mtllib(fastObjData* data, void* file)
|
|||
p++;
|
||||
|
||||
s = p;
|
||||
while (!is_end_of_name(*p))
|
||||
p++;
|
||||
p = skip_name(p);
|
||||
|
||||
mtl.name = string_copy(s, p);
|
||||
}
|
||||
|
@ -1092,7 +1190,7 @@ int read_mtllib(fastObjData* data, void* file)
|
|||
if (is_whitespace(*p))
|
||||
p = read_map(data, p, &mtl.map_d);
|
||||
}
|
||||
else if (p[0] == 'b' &&
|
||||
else if ((p[0] == 'b' || p[0] == 'B') &&
|
||||
p[1] == 'u' &&
|
||||
p[2] == 'm' &&
|
||||
p[3] == 'p' &&
|
||||
|
@ -1121,7 +1219,7 @@ int read_mtllib(fastObjData* data, void* file)
|
|||
|
||||
|
||||
static
|
||||
const char* parse_mtllib(fastObjData* data, const char* ptr)
|
||||
const char* parse_mtllib(fastObjData* data, const char* ptr, const fastObjCallbacks* callbacks, void* user_data)
|
||||
{
|
||||
const char* s;
|
||||
const char* e;
|
||||
|
@ -1132,9 +1230,7 @@ const char* parse_mtllib(fastObjData* data, const char* ptr)
|
|||
ptr = skip_whitespace(ptr);
|
||||
|
||||
s = ptr;
|
||||
while (!is_end_of_name(*ptr))
|
||||
ptr++;
|
||||
|
||||
ptr = skip_name(ptr);
|
||||
e = ptr;
|
||||
|
||||
lib = string_concat(data->base, s, e);
|
||||
|
@ -1142,11 +1238,11 @@ const char* parse_mtllib(fastObjData* data, const char* ptr)
|
|||
{
|
||||
string_fix_separators(lib);
|
||||
|
||||
file = file_open(lib);
|
||||
file = callbacks->file_open(lib, user_data);
|
||||
if (file)
|
||||
{
|
||||
read_mtllib(data, file);
|
||||
file_close(file);
|
||||
read_mtllib(data, file, callbacks, user_data);
|
||||
callbacks->file_close(file, user_data);
|
||||
}
|
||||
|
||||
memory_dealloc(lib);
|
||||
|
@ -1157,7 +1253,7 @@ const char* parse_mtllib(fastObjData* data, const char* ptr)
|
|||
|
||||
|
||||
static
|
||||
void parse_buffer(fastObjData* data, const char* ptr, const char* end)
|
||||
void parse_buffer(fastObjData* data, const char* ptr, const char* end, const fastObjCallbacks* callbacks, void* user_data)
|
||||
{
|
||||
const char* p;
|
||||
|
||||
|
@ -1207,6 +1303,21 @@ void parse_buffer(fastObjData* data, const char* ptr, const char* end)
|
|||
}
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
p++;
|
||||
|
||||
switch (*p++)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
p = parse_object(data, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
p--; /* roll p++ back in case *p was a newline */
|
||||
}
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
p++;
|
||||
|
||||
|
@ -1230,7 +1341,7 @@ void parse_buffer(fastObjData* data, const char* ptr, const char* end)
|
|||
p[3] == 'i' &&
|
||||
p[4] == 'b' &&
|
||||
is_whitespace(p[5]))
|
||||
p = parse_mtllib(data, p + 5);
|
||||
p = parse_mtllib(data, p + 5, callbacks, user_data);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
|
@ -1252,6 +1363,15 @@ void parse_buffer(fastObjData* data, const char* ptr, const char* end)
|
|||
|
||||
data->line++;
|
||||
}
|
||||
if (array_size(data->mesh->colors) > 0)
|
||||
{
|
||||
/* Fill the remaining slots in the colors array */
|
||||
unsigned int ii;
|
||||
for (ii = array_size(data->mesh->colors); ii < array_size(data->mesh->positions); ++ii)
|
||||
{
|
||||
array_push(data->mesh->colors, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1260,26 +1380,47 @@ void fast_obj_destroy(fastObjMesh* m)
|
|||
unsigned int ii;
|
||||
|
||||
|
||||
for (ii = 0; ii < array_size(m->objects); ii++)
|
||||
object_clean(&m->objects[ii]);
|
||||
|
||||
for (ii = 0; ii < array_size(m->groups); ii++)
|
||||
group_clean(&m->groups[ii]);
|
||||
|
||||
for (ii = 0; ii < array_size(m->materials); ii++)
|
||||
mtl_clean(&m->materials[ii]);
|
||||
|
||||
for (ii = 0; ii < array_size(m->textures); ii++)
|
||||
map_clean(&m->textures[ii]);
|
||||
|
||||
array_clean(m->positions);
|
||||
array_clean(m->texcoords);
|
||||
array_clean(m->normals);
|
||||
array_clean(m->colors);
|
||||
array_clean(m->face_vertices);
|
||||
array_clean(m->face_materials);
|
||||
array_clean(m->indices);
|
||||
array_clean(m->objects);
|
||||
array_clean(m->groups);
|
||||
array_clean(m->materials);
|
||||
array_clean(m->textures);
|
||||
|
||||
memory_dealloc(m);
|
||||
}
|
||||
|
||||
|
||||
fastObjMesh* fast_obj_read(const char* path)
|
||||
{
|
||||
fastObjCallbacks callbacks;
|
||||
callbacks.file_open = file_open;
|
||||
callbacks.file_close = file_close;
|
||||
callbacks.file_read = file_read;
|
||||
callbacks.file_size = file_size;
|
||||
|
||||
return fast_obj_read_with_callbacks(path, &callbacks, 0);
|
||||
}
|
||||
|
||||
|
||||
fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbacks* callbacks, void* user_data)
|
||||
{
|
||||
fastObjData data;
|
||||
fastObjMesh* m;
|
||||
|
@ -1289,12 +1430,15 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
char* end;
|
||||
char* last;
|
||||
fastObjUInt read;
|
||||
size_t sep;
|
||||
fastObjUInt bytes;
|
||||
|
||||
/* Check if callbacks are valid */
|
||||
if(!callbacks)
|
||||
return 0;
|
||||
|
||||
|
||||
/* Open file */
|
||||
file = file_open(path);
|
||||
file = callbacks->file_open(path, user_data);
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
|
@ -1307,14 +1451,17 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
m->positions = 0;
|
||||
m->texcoords = 0;
|
||||
m->normals = 0;
|
||||
m->colors = 0;
|
||||
m->face_vertices = 0;
|
||||
m->face_materials = 0;
|
||||
m->indices = 0;
|
||||
m->materials = 0;
|
||||
m->textures = 0;
|
||||
m->objects = 0;
|
||||
m->groups = 0;
|
||||
|
||||
|
||||
/* Add dummy position/texcoord/normal */
|
||||
/* Add dummy position/texcoord/normal/texture */
|
||||
array_push(m->positions, 0.0f);
|
||||
array_push(m->positions, 0.0f);
|
||||
array_push(m->positions, 0.0f);
|
||||
|
@ -1326,9 +1473,12 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
array_push(m->normals, 0.0f);
|
||||
array_push(m->normals, 1.0f);
|
||||
|
||||
array_push(m->textures, map_default());
|
||||
|
||||
|
||||
/* Data needed during parsing */
|
||||
data.mesh = m;
|
||||
data.object = object_default();
|
||||
data.group = group_default();
|
||||
data.material = 0;
|
||||
data.line = 1;
|
||||
|
@ -1336,8 +1486,16 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
|
||||
|
||||
/* Find base path for materials/textures */
|
||||
if (string_find_last(path, FAST_OBJ_SEPARATOR, &sep))
|
||||
data.base = string_substr(path, 0, sep + 1);
|
||||
{
|
||||
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 */
|
||||
|
@ -1349,7 +1507,7 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
for (;;)
|
||||
{
|
||||
/* Read another buffer's worth from file */
|
||||
read = (fastObjUInt)(file_read(file, start, BUFFER_SIZE));
|
||||
read = (fastObjUInt)(callbacks->file_read(file, start, BUFFER_SIZE, user_data));
|
||||
if (read == 0 && start == buffer)
|
||||
break;
|
||||
|
||||
|
@ -1384,7 +1542,7 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
|
||||
|
||||
/* Process buffer */
|
||||
parse_buffer(&data, buffer, last);
|
||||
parse_buffer(&data, buffer, last, callbacks, user_data);
|
||||
|
||||
|
||||
/* Copy overflow for next buffer */
|
||||
|
@ -1394,15 +1552,22 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
}
|
||||
|
||||
|
||||
/* Flush final group */
|
||||
flush_output(&data);
|
||||
/* Flush final object/group */
|
||||
flush_object(&data);
|
||||
object_clean(&data.object);
|
||||
|
||||
flush_group(&data);
|
||||
group_clean(&data.group);
|
||||
|
||||
m->position_count = array_size(m->positions) / 3;
|
||||
m->texcoord_count = array_size(m->texcoords) / 2;
|
||||
m->normal_count = array_size(m->normals) / 3;
|
||||
m->color_count = array_size(m->colors) / 3;
|
||||
m->face_count = array_size(m->face_vertices);
|
||||
m->index_count = array_size(m->indices);
|
||||
m->material_count = array_size(m->materials);
|
||||
m->texture_count = array_size(m->textures);
|
||||
m->object_count = array_size(m->objects);
|
||||
m->group_count = array_size(m->groups);
|
||||
|
||||
|
||||
|
@ -1410,7 +1575,7 @@ fastObjMesh* fast_obj_read(const char* path)
|
|||
memory_dealloc(buffer);
|
||||
memory_dealloc(data.base);
|
||||
|
||||
file_close(file);
|
||||
callbacks->file_close(file, user_data);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue