Compare commits

...

47 commits
v1.0 ... master

Author SHA1 Message Date
Richard Knight
9884aa86b0
Merge pull request #51 from corporateshark/master
Update CMake file to CMake version `3.5...3.31`
2024-12-10 08:53:05 +00:00
Sergey Kosarevsky
fce8123891 Update CMake file to CMake version 3.5...3.31
This approach allows us to maintain compatibility with the minimum supported CMake version 3.5,
while suppressing all policy warnings up to the latest version 3.31.
2024-12-06 16:46:51 -08:00
Richard Knight
8145eb0a94
Merge pull request #50 from corporateshark/master
Update CMake file to CMake version 3.5
2024-11-19 20:38:52 +00:00
Sergey Kosarevsky
ddfe2e7f86 Update CMake file to CMake version 3.5
Fixed this deprecation warning:
``
CMake Deprecation Warning at CMakeLists.txt:1 (CMAKE_MINIMUM_REQUIRED):
Compatibility with CMake < 3.5 will be removed from a future version of CMake.
``
2024-11-03 22:40:54 -08:00
Richard Knight
42629f7442
Small note about API change in 1.3 2024-06-04 20:31:38 +01:00
Richard Knight
9e2c3677b6 Bump version number to 1.3 2024-06-04 20:27:55 +01:00
Richard Knight
0ba37c7ff9
Merge pull request #36 from laurelkeys/patch-1
Proposal: move material texture maps into fastObjMesh
2024-06-04 20:21:10 +01:00
Tiago Chaves
12b04fc1ab
Merge branch 'master' into patch-1 2023-12-30 14:05:57 -05:00
Richard Knight
724e09ff1e Fix fuzzer test when vertex index is garbage 2023-12-21 17:17:30 +00:00
Richard Knight
c8707104b8
Merge pull request #46 from DavidKorczynski/clusterfuzzlite-intergation
Add ClusterFuzzLite integration
2023-12-21 16:38:36 +00:00
Richard Knight
e24d2c5533
Merge pull request #45 from zeux/master
Mark materials that weren't loaded from mtllib as fallback
2023-12-21 16:33:03 +00:00
David Korczynski
bcc68cca4c Add ClusterFuzzLite integration
Signed-off-by: David Korczynski <david@adalogics.com>
2023-12-21 04:19:45 -08:00
Arseny Kapoulkine
b827219dbf Mark materials that weren't loaded from mtllib as fallback
This can happen when mtllib is absent, points to a non-existing file, or
material names are incorrect. In either case it helps the reader
differentiate between materials that are actually specified in the file,
even if their definition happens to match the default, from materials
that were never loaded in the first place.
2023-12-15 11:29:56 -08:00
Richard Knight
36cb5a8d89
Merge pull request #43 from zeux/skip_name
Trim trailing whitespace for names
2023-08-14 20:14:40 +01:00
Arseny Kapoulkine
33e0ee7162 Trim trailing whitespace for names
Some .obj files have extra whitespace after usemtl and other statements;
this may interfere with parsing, for example by adding a space to the
material name which can result in inability to find it in .mtl file.

For consistency, we replace all uses of is_end_of_name with skip_name
that handles this by leaving trailing whitespace alone. It will be
skipped in the parsing flow when we skip the newline, which is similar
to the behavior of trailing whitespace after numeric data (which
parse_float/int leave alone).
2023-08-13 10:08:29 -07:00
Richard Knight
2bb0b1f650
Merge pull request #41 from maxrigout/master
added support for colored vertices (issue #28)
2023-08-08 10:52:23 +01:00
Max Rigout
491efbbb59 removed extra white spaces 2023-08-03 10:17:31 -04:00
Max Rigout
cd5be148c3 re-added mesh normal initialization that was accidently removed) 2023-08-03 08:40:58 -04:00
Max Rigout
27803406d7 reverted color parsing to original approach 2023-08-03 08:31:16 -04:00
Max Rigout
20a9db53a0
Merge pull request #1 from maxrigout/colors
Colors
2023-08-02 22:34:25 -04:00
Max Rigout
2c91cb2993 added lazy fill for vertex_color_index 2023-08-02 22:18:31 -04:00
Max Rigout
9037820f30 improved color parsing 2023-08-02 22:17:37 -04:00
Max Rigout
a511f60249 added support for colored vertices (issue #28) 2023-07-31 21:23:40 -04:00
Richard Knight
1a8060257a
Merge pull request #40 from zeux/master
Fix 32-bit integer overflow issues
2023-06-10 12:57:54 +01:00
Arseny Kapoulkine
66f467de8f Fix 32-bit integer overflow issues
When an array reached a size that would require >4GB allocation, the
argument to realloc would overflow during multiplication, resulting in a
very small allocation and a subsequent out of bounds access.

This change fixes that and also adjusts capacity calculation so that it
doesn't overflow 32-bit range until it's basically impossible not to.

This is not perfect - in particular, on 32-bit systems there's a risk of
size_t overflow that remains, however because we grow in 1.5x
increments, realistically an attempt to grow a 2GB allocation to the
next increment would fail before that. We can also technically overflow
capacity even after the adjustment, but that requires 3+B elements which
effectively means an .obj file on the scale of hundreds of gigabytes, at
which point maybe a streaming parser would be more practical.
2023-06-08 20:35:25 -07:00
Tiago Chaves
6f4e843882
Move material texture maps into fastObjMesh 2022-08-29 11:24:49 -03:00
Richard Knight
85778da5fc
Merge pull request #31 from cadenji/add-index_count
Add index_count member to fastObjMesh struct
2022-01-29 16:42:41 +00:00
cadenji
3a1efbcc79 Add index_count member to fastObjMesh struct
In some cases we need to know the length of `indices`.
Currently need to calculate the sum of the `face_vertices`
elements to get this value.

It would be convenient if we could get the value directly.
2022-01-29 23:15:20 +08:00
Richard Knight
d2c273248a Use the same struct definition for Object and Group. 2021-10-19 08:54:00 +01:00
Richard Knight
ed03b86e7f Fix stupid face counting bug in objects 2021-10-19 08:44:07 +01:00
Richard Knight
9f4b38eb1b Add support for object names 2021-10-18 15:56:38 +01:00
Richard Knight
49c810ae1c Bump version number 2021-08-01 14:07:18 +01:00
Richard Knight
86d7dc6e43 Update CMake file so it can be used with FetchContent in other projects 2021-08-01 14:06:21 +01:00
Richard Knight
92551724cf Avoid signed overflow with crazy exponents. Fixes #27. 2021-06-01 18:23:53 +01:00
Richard Knight
d97389aa2d
Merge pull request #26 from zeux/patch-1
Fix version number in the comment
2021-04-01 18:09:38 +01:00
Arseny Kapoulkine
fed0e5760f
Fix version number in the comment
Also fix copyright year while I'm at this.
2021-03-31 22:55:39 -07:00
Richard Knight
5d1a8529cf Tidy formatting, bump version number. 2021-03-13 08:23:38 +00:00
Richard Knight
eba6f59206 Fix #25. Check for 'map_Bump' as well as 'map_bump'. 2021-03-13 08:21:43 +00:00
Richard Knight
e7a99e63a0
Merge pull request #23 from BeastLe9enD/master
Virtual filesystem support
2021-03-06 08:49:26 +00:00
BeastLe9enD
723e0e4775 Fixes 2021-03-05 23:07:01 +01:00
BeastLe9enD
597b5a8420 Some fixes 2021-03-05 00:56:31 +01:00
BeastLe9enD
cf2d64dea3 Virtual filesystem support 2021-03-04 20:25:38 +01:00
Richard Knight
ac312c9c6d
Merge pull request #21 from zeux/master
Fix base path handling for mixed slashes
2021-01-17 19:50:29 +00:00
Arseny Kapoulkine
a40b84252a Fix base path handling for mixed slashes
On Windows, paths with mixed slashes weren't processed correctly as `\`
would be picked if it was anywhere in the path for the purpose of
determining base.

This also removes the #ifdef _WIN32 from the logic; this helps on
platforms like Emscripten where running the resulting binary that
wasn't compiled with WIN32 still needs backslash processing; paths with
backslashes on Linux should be exceedingly rare, plus we *already*
correct backslashes with forward slashes on Linux anyway...
2021-01-17 10:27:44 -08:00
Richard Knight
0f90b6fa0f Allow both direction of slash as a path separator for initial path on Windows 2020-10-28 22:07:20 +00:00
Richard Knight
2c9e41aed3
Merge pull request #19 from AurL/bare-obj-materials
Create materials even if not definition provided
2020-10-20 18:33:17 +01:00
Aurélien Chatelain
76143f7ef2 Create materials for 'usemtl' declaration
Problem: I read a bare OBJ so no MTL is provided. I still want
to keep the materials setup across the scene for further edit.

Fix: Instead of using a fallback material (idx 0), create a material
for each not-defined material while keeping the name
so that the structure of the model is conserved.
2020-10-20 07:52:02 +00:00
9 changed files with 380 additions and 130 deletions

View 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

View file

@ -0,0 +1,3 @@
# ClusterFuzzLite set up
This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite).

View 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

View 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;
}

View file

@ -0,0 +1 @@
language: c++

30
.github/workflows/cflite_pr.yml vendored Normal file
View 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 }}

View file

@ -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()

View file

@ -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.

View file

@ -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;
}