99 Commits

Author SHA1 Message Date
Syoyo Fujita
f37fed32f3 Merge pull request #196 from jasjuang/master
Use CMake built-in variable BUILD_SHARED_LIBS instead of custom TINYOBJLOADER_COMPILATION_SHARED
2019-01-05 15:52:55 +09:00
jasjuang
85d92bb5cb use cmake default variable BUILD_SHARED_LIBS instead of custom TINYOBJLOADER_COMPILATION_SHARED 2019-01-04 11:06:27 -08:00
Syoyo Fujita
40d00fd935 Merge pull request #195 from Neptilo/master
Fix crash when reading mtllib command
2019-01-04 22:13:05 +09:00
Neptilo
7712f1bebd Fix crash when reading mtllib command
The code was assuming the mtllib command to be near the beginning of the file, and therefore to always be read by the first thread.
2019-01-04 13:57:34 +01:00
Syoyo Fujita
e96e994355 Merge pull request #194 from jasjuang/master
namespace for cmake export
2019-01-04 12:33:19 +09:00
jasjuang
8f16866c37 namespace for cmake export 2019-01-03 19:26:00 -08:00
Syoyo Fujita
5e668b398a Merge pull request #193 from scpeters/patch-1
tiny_obj_loader.h: initialize pad_ in constructor
2018-12-15 01:30:19 +09:00
Steven Peters
df3bb6f8e8 tiny_obj_loader.h: fix order of variables 2018-12-13 10:49:18 -08:00
Steven Peters
546aa09d8d tiny_obj_loader.h: initialize pad_ in constructor 2018-12-11 15:09:56 -08:00
Syoyo Fujita
aa90b5466f Merge pull request #192 from basicNew/ChangeLoopTerminationConstraint
Change triangulation loop termination constraint
2018-12-08 17:57:06 +09:00
Andres Fortier
b404f3af67 Change loop termination constraint 2018-12-07 17:06:38 -03:00
Syoyo Fujita
a957ebe002 Fix some coments.
Bump version 1.4.0
2018-12-01 13:32:05 +09:00
Syoyo Fujita
d793bfb405 Merge pull request #191 from sinisterchipmunk/initialize_unparsed_texopts
Properly initialize all texture_option_t's, not just parsed ones
2018-12-01 13:25:41 +09:00
Colin MacKenzie IV
0ab0146296 Update function declaration 2018-11-30 15:27:38 -05:00
Colin MacKenzie IV
20d122f305 Properly initialize all texture_option_t's, not just parsed ones 2018-11-30 15:14:07 -05:00
Syoyo Fujita
850d0ffdfa Merge pull request #190 from ukabuer/patch-1
Update README.md
2018-11-30 21:29:21 +09:00
UKABUER
80058fdcb0 Update README.md 2018-11-30 20:24:14 +08:00
Syoyo Fujita
1a7aea4ac1 Merge pull request #189 from mathue/patch-1
Fix of important memory leak in Python module
2018-11-29 01:31:03 +09:00
Martin Thümmel
178ef391c7 Fix of important memory leak in Python module
Dear all,
As described in the issue https://github.com/syoyo/tinyobjloader/issues/188#issue-385341218 there is a memory leak in the function pyLoadObj(). The reference count for all created Python objects must be decreased after they are fed into the lists and dictionaries. The only  exception from this is the function PyTuple_SetItem() in pyTupleFromfloat3(), which decreases the reference counter of the Python object *tuple automatically by calling this function. In all other cases like PyList_Insert(), the references are only borrowed and not decreased. Therefore, each created Python object will remain in the heap, since there is one reference to it left in the counter.

These facts are explained in https://docs.python.org/2/c-api/intro.html#reference-counts in detail.

Best regards
Martin.
P.S. sorry, that i did not put that much effort into a more readable code and just inserted the Py_DECREF() function at every necessary position.
2018-11-28 17:16:12 +01:00
Syoyo Fujita
59b4d7ccef Merge pull request #187 from Neptilo/master
Fix parsing double with negative exponent
2018-11-06 00:28:10 +09:00
Victor Ripplinger
6f990e2b6c Fix parsing double with negative exponent 2018-11-05 15:17:34 +01:00
Syoyo Fujita
7fb5056a53 Make ParseTextureNameAndOption function public. 2018-10-19 15:29:31 +09:00
Syoyo Fujita
934788785e Show line number for some warning and error message. 2018-10-10 13:33:11 +09:00
Syoyo Fujita
e07a835f02 Separate error message into warning and error. Breaks API, so bump to version 1.3.0 2018-10-10 01:56:07 +09:00
Syoyo Fujita
1cdfd786d8 Add colorspace extension to texture options. Fixes #184 2018-09-14 13:42:27 +09:00
Syoyo Fujita
803b65b8a0 Merge pull request #183 from mlimper/master
Added option to disable default vertex colors
2018-09-01 00:31:29 +09:00
Max Limper
50518a515b added option to disable default vertex colors 2018-08-31 16:59:32 +02:00
Syoyo Fujita
c9b1bccf97 Fix mis-counting of faces when triangulation is enabled. 2018-08-20 15:10:37 +09:00
Syoyo Fujita
1f17833657 Add comments. 2018-08-20 14:24:49 +09:00
Syoyo Fujita
bb58a8f8c3 Revert assertion equation. 2018-08-20 14:19:34 +09:00
Syoyo Fujita
b1f594d682 Merge branch 'master' of github.com:syoyo/tinyobjloader 2018-08-20 14:10:17 +09:00
Syoyo Fujita
02df4943f9 Fix assert equation. 2018-08-20 14:09:56 +09:00
Syoyo Fujita
826a892d0b Merge pull request #181 from vincentdm05/missing-data
Produce an error and return when indices and data dont match.
2018-08-17 16:12:36 +09:00
Vincent de Marignac
68350e2fc7 give respective warnings but allow partially correct data to be returned 2018-08-16 23:34:47 +03:00
Vincent de Marignac
7d20e9b901 use ternary op instead of max from omitted lib 2018-08-16 00:00:22 +03:00
Vincent de Marignac
fdc70abdc6 fix index comparison wrt array size 2018-08-15 23:41:35 +03:00
Vincent de Marignac
8a885e14b8 Produce an error and return when indices and data dont match. 2018-08-15 23:13:00 +03:00
Syoyo Fujita
cd65de860b Merge branch 'master' of github.com:syoyo/tinyobjloader 2018-08-13 17:47:38 +09:00
Syoyo Fujita
4924857fd3 Add note on # of max threads. 2018-08-13 17:47:13 +09:00
Syoyo Fujita
1a7bdc6192 Parse multiple group names. Fixes #146 2018-08-11 15:21:17 +09:00
Syoyo Fujita
fd06fa49e4 Add unit test for parsing 'l'. 2018-07-25 21:42:23 +09:00
Syoyo Fujita
0d68262246 Fix buffer overrun when parsing 'l' line. 2018-07-25 16:24:40 +09:00
Syoyo Fujita
0e950513a3 Merge pull request #178 from ZKing1000/master
Added support for line paths.
2018-07-25 16:02:49 +09:00
Holden Green
a4b115a584 Hopefully fixed compiling error 2018-07-24 23:47:59 -07:00
Syoyo Fujita
bb3e27d4f3 Merge pull request #179 from mlimper/master
Fixed #177
2018-07-09 19:29:07 +09:00
Max Limper
c4e7e65acb fixed #177 (explicit empty mtl base dir given) 2018-07-09 12:21:46 +02:00
Max Limper
eba327b9c0 make tester fail for issue 177 failing (explicit MTL base dir given by API user) 2018-07-09 12:19:33 +02:00
Holden Green
3cbf45a572 Non const 2018-07-04 22:13:39 -07:00
Holden Green
6650dbf397 Used swap in favor of move 2018-07-04 22:10:45 -07:00
Holden Green
7befd59de4 Removed comment 2018-07-04 21:59:56 -07:00
Holden Green
c5b3139653 Fixed comments 2018-07-04 21:46:42 -07:00
Holden Green
adb2309110 Removed print message. 2018-07-04 21:44:09 -07:00
Holden Green
3edca81a75 Added line paths. 2018-07-04 21:21:56 -07:00
Syoyo Fujita
7a88cddefc Add unit test for issue 177. 2018-07-03 18:37:41 +02:00
Syoyo Fujita
d541711a79 Merge pull request #176 from silverweed/master
Ensure mtl_basedir ends with a directory separator
2018-06-02 00:18:54 +09:00
Syoyo Fujita
d1ce2082f6 Remove afl files since it fails to create file on Windows file system. 2018-06-02 00:15:18 +09:00
silverweed
8fd9f6e57b ensure mtl_basedir ends with a directory separator 2018-06-01 12:02:38 +02:00
Syoyo Fujita
24bd8b49ff Merge branch 'master' of github.com:syoyo/tinyobjloader 2018-05-31 16:34:01 +09:00
Syoyo Fujita
4a0e79985d Bump version. 2018-05-31 16:29:14 +09:00
Syoyo Fujita
06f6139d1f Limit # of tags when parsing SubD tag('t'). 2018-05-31 16:25:39 +09:00
Syoyo Fujita
0c8db8ee23 Hardened implementation(fix some seg faults, out-of-bound access) found by fuzzer. 2018-05-31 15:49:56 +09:00
Syoyo Fujita
64d1e3f883 Merge pull request #170 from Neptilo/master
Fix bug that discarded some chunks.
2018-03-27 00:57:19 +09:00
Victor Ripplinger
a39a6b481c Fix bug that discarded some chunks.
The OBJ loader was discarding the first line of a file
or a chunk following one that ends with a newline.
2018-03-26 16:28:13 +02:00
Syoyo Fujita
bce1bb8387 Merge branch 'master' of github.com:syoyo/tinyobjloader 2018-03-26 20:10:55 +09:00
Syoyo Fujita
7fcfafb39a Fix compilation for double precision mode(Fixes #167). 2018-03-26 20:09:56 +09:00
Syoyo Fujita
5eda671225 Merge pull request #169 from LZaw/master
Adapt 1.0.x format
2018-03-10 00:03:35 +09:00
LZaw
f95510b04b Removed multiplication for color_idx_offset 2018-03-09 13:57:18 +01:00
LZaw
94f1dc15b3 Update obj_sticher.cc 2018-03-09 13:48:05 +01:00
LZaw
a4eabf54b1 Adapt 1.0.x datastructure 2018-03-09 13:43:52 +01:00
Syoyo Fujita
72c38f64d5 Merge pull request #168 from LZaw/master
Adapt the v1.0.x data format to the WriteObj function
2018-03-09 00:57:27 +09:00
LZaw
d174e625a2 Update obj_writer.h 2018-03-08 15:58:39 +01:00
LZaw
707014f843 Update obj_writer.cc 2018-03-08 15:48:45 +01:00
LZaw
12837cc8b2 Adapt v 1.0.x
Added an attributes parameter
2018-03-08 15:47:06 +01:00
Syoyo Fujita
b85714b4cf Skip parsing incomplete or invalid face definition(e.g. f definition only contains 1 or 2 indices). 2018-02-23 20:25:13 +09:00
Syoyo Fujita
e060b4f4aa Remove unused variable. 2018-02-21 20:53:30 +09:00
Syoyo Fujita
2dca72724f Merge pull request #165 from Neptilo/master
Fix bug in not assigning material id to faces
2018-02-21 20:01:23 +09:00
Victor Ripplinger
72529f02fe Material ids were not assigned to faces if the
first face with this material was in another thread buffer than where
the usemtl command was
2018-02-21 11:36:36 +01:00
Syoyo Fujita
16f3041c76 Merge pull request #163 from Neptilo/master
Fix bug when line starts before a chunk and ends after it
2018-02-09 20:10:16 +09:00
Neptilo
73e9b4dc3a Fix bug when line starts before a chunk and ends after it
When a line started in a chunk, the algorithm only looked for its end in the following chunk, but the end of the line may actually be located even further. Instead, in this commit we look for the next new line until the end of the whole buffer.
2018-02-09 11:46:43 +01:00
Syoyo Fujita
5113cd65cf print smoothing group id. 2018-02-04 15:19:42 +09:00
Syoyo Fujita
65df7c4794 Add smooting group test .obj with two squares. 2018-02-04 14:16:14 +09:00
Syoyo Fujita
eaf8623e61 Fix build of viewer example.
Bump version.
2018-02-04 14:05:36 +09:00
Syoyo Fujita
dcad3e6c50 Add per-face smoothing groupd ids to mesh_t.
Add unit test for smoothing groups.
2018-01-31 22:13:52 +09:00
Syoyo Fujita
2dfc37a475 Introduce face_t structure which stores per-face smoothing group and vertex indices. 2018-01-31 20:07:34 +09:00
Syoyo Fujita
9a6390cdee Add padding. 2018-01-31 14:24:37 +09:00
gopalss
78c7c9011a Fix for #29 Normal Generation by Smoothing Group Id - Commit 4 2018-01-30 10:55:10 -05:00
gopalss
1754669b07 Fix for #29 Normal Generation by Smoothing Group Id - Commit 3 2018-01-30 10:31:02 -05:00
gopalss
1f7b4a49c0 Fix for #29 Normal Generation by Smoothing Group Id - Commit 2 2018-01-30 09:51:16 -05:00
gopalss
15f47e2e35 Fix for #29 Normal Generation by Smoothing Group Id 2018-01-29 18:37:39 -05:00
Syoyo Fujita
12bf4165be Fix assertion when there is no normal/texcoord assigned to the face. Fixes #161.
Suppress clang warnings.
2018-01-29 13:41:33 +09:00
Syoyo Fujita
f206a56362 Merge pull request #160 from arosh/master
PR for open bounty test 2.
2018-01-17 01:56:07 +09:00
Sho Iizuka
13951d6459 Remove empty lines at EOF 2018-01-16 18:11:05 +09:00
Syoyo Fujita
5383e3400a Merge pull request #156 from raspofabs/master
ear clipping using pnpoly algorithm
2018-01-15 17:26:11 +09:00
Richard Fabian
ac3c36ffda increase the size of the epsilon to give better axis choice for triangulation 2018-01-14 18:53:20 +00:00
Richard Fabian
3b681805aa fixed the bug where the wrong axis was selected for doing the pnpoly check 2018-01-14 16:45:59 +00:00
Richard Fabian
ca49183639 added ear clipping triangulation 2018-01-14 14:21:46 +00:00
Syoyo Fujita
b29d34f2e1 Merge pull request #154 from ogrex/master
Bugfix for multiple texture
2017-12-21 01:23:03 +09:00
ogrex
cb085d1fb6 fix multiple texture wrong id problem 2017-12-20 22:17:19 +09:00
ogrex
b38e97b7ec trimming '\r' in material_file name 2017-12-20 22:13:58 +09:00
31 changed files with 2701 additions and 1750 deletions

View File

@@ -46,15 +46,13 @@ set(TINYOBJLOADER_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
set(TINYOBJLOADER_RUNTIME_DIR ${CMAKE_INSTALL_BINDIR})
option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Example Loader Application" OFF)
option(TINYOBJLOADER_COMPILATION_SHARED "Build as shared library" OFF)
if(TINYOBJLOADER_COMPILATION_SHARED)
add_library(${LIBRARY_NAME} SHARED ${tinyobjloader-Source})
add_library(${LIBRARY_NAME} ${tinyobjloader-Source})
if(BUILD_SHARED_LIBS)
set_target_properties(${LIBRARY_NAME} PROPERTIES
SOVERSION ${TINYOBJLOADER_SOVERSION}
)
else()
add_library(${LIBRARY_NAME} STATIC ${tinyobjloader-Source})
endif()
set_target_properties(${LIBRARY_NAME} PROPERTIES VERSION ${TINYOBJLOADER_VERSION})
@@ -120,6 +118,8 @@ install(TARGETS
)
install(EXPORT
${PROJECT_NAME}-targets
NAMESPACE
tinyobjloader::
DESTINATION
${TINYOBJLOADER_CMAKE_DIR}
)

View File

@@ -98,6 +98,7 @@ TinyObjLoader is successfully used in ...
* PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
* Callback API for custom loading.
* Double precision support(for HPC application).
* Smoothing group
## TODO
@@ -105,8 +106,6 @@ TinyObjLoader is successfully used in ...
* [ ] Fix obj_sticker example.
* [ ] More unit test codes.
* [x] Texture options
* [ ] Normal vector generation
* [ ] Support smoothing groups
## License
@@ -206,8 +205,9 @@ tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, inputfile.c_str());
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str());
if (!err.empty()) { // `err` may contain warning message.
std::cerr << err << std::endl;

View File

@@ -129,6 +129,7 @@ int main(int argc, char **argv) {
cb.object_cb = object_cb;
MyMesh mesh;
std::string warn;
std::string err;
std::string filename = "../../models/cornell_box.obj";
if (argc > 1) {
@@ -143,7 +144,11 @@ int main(int argc, char **argv) {
tinyobj::MaterialFileReader mtlReader("../../models/");
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err);
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &warn, &err);
if (!warn.empty()) {
std::cout << "WARN: " << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;

View File

@@ -1,9 +1,10 @@
//
// Stiches multiple .obj files into one .obj.
//
#include "../../tiny_obj_loader.h"
#include "obj_writer.h"
#include "../../tiny_obj_loader.h"
#include <cassert>
#include <iostream>
#include <cstdlib>
@@ -11,27 +12,59 @@
typedef std::vector<tinyobj::shape_t> Shape;
typedef std::vector<tinyobj::material_t> Material;
typedef tinyobj::attrib_t Attribute;
void
StichObjs(
tinyobj::attrib_t& out_attribute,
std::vector<tinyobj::shape_t>& out_shape,
std::vector<tinyobj::material_t>& out_material,
const std::vector<Attribute>& attributes,
const std::vector<Shape>& shapes,
const std::vector<Material>& materials)
{
int numShapes = 0;
for (size_t i = 0; i < shapes.size(); i++) {
numShapes += (int)shapes[i].size();
// The amount of attributes, shape-vectors and material-vecotrs should be the same.
if(attributes.size() != shapes.size() && attributes.size() != materials.size()){
std::cerr << "Size of attributes, shapes and Materials don't fit!" << attributes.size() << " " << shapes.size() <<" " << materials.size() << std::endl;;
exit(1);
}
int num_shapes = 0;
// 4 values (vertices, normals, texcoords, colors)
std::vector<int> num_attributes(4, 0);
int num_materials = 0;
for(int i = 0; i < shapes.size(); i++){
num_shapes += shapes[i].size();
}
for(int i = 0; i < attributes.size(); i++){
num_attributes[0] += attributes[i].vertices.size();
num_attributes[1] += attributes[i].normals.size();
num_attributes[2] += attributes[i].texcoords.size();
num_attributes[3] += attributes[i].colors.size();
}
for(int i = 0; i < materials.size(); i++){
num_materials += materials[i].size();
}
printf("Total # of shapes = %d\n", numShapes);
int materialIdOffset = 0;
// More performant, than push_back
out_attribute.vertices.resize(num_attributes[0]);
out_attribute.normals.resize(num_attributes[1]);
out_attribute.texcoords.resize(num_attributes[2]);
out_attribute.colors.resize(num_attributes[3]);
out_shape.resize(num_shapes);
out_material.resize(num_materials);
size_t face_offset = 0;
int material_id_offset = 0;
int shape_id_offset = 0;
int vertex_idx_offset = 0;
int normal_idx_offset = 0;
int texcoord_idx_offset = 0;
int color_idx_offset = 0;
// shapes.size() = attributes.size() = materials.size()
for (size_t i = 0; i < shapes.size(); i++) {
// Copy shapes
for (size_t k = 0; k < shapes[i].size(); k++) {
std::string new_name = shapes[i][k].name;
// Add suffix
char buf[1024];
@@ -39,36 +72,51 @@ StichObjs(
new_name += std::string(buf);
printf("shape[%ld][%ld].name = %s\n", i, k, shapes[i][k].name.c_str());
assert((shapes[i][k].mesh.indices.size() % 3) == 0);
assert((shapes[i][k].mesh.positions.size() % 3) == 0);
tinyobj::shape_t new_shape = shapes[i][k];
// Add offset.
for (size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) {
new_shape.mesh.material_ids[f] += materialIdOffset;
// Add material offset.
for(size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) {
new_shape.mesh.material_ids[f] += material_id_offset;
}
// Add indices offset.
for(size_t f = 0; f < new_shape.mesh.indices.size(); f++){
tinyobj::index_t& ref = new_shape.mesh.indices[f];
if(ref.vertex_index > -1){
ref.vertex_index += vertex_idx_offset;
}
if(ref.normal_index > -1){
ref.normal_index += normal_idx_offset;
}
if(ref.texcoord_index > -1){
ref.texcoord_index += texcoord_idx_offset;
}
}
new_shape.name = new_name;
printf("shape[%ld][%ld].new_name = %s\n", i, k, new_shape.name.c_str());
out_shape.push_back(new_shape);
out_shape[shape_id_offset++] = new_shape;
}
materialIdOffset += materials[i].size();
}
for (size_t i = 0; i < materials.size(); i++) {
// Copy materials
for (size_t k = 0; k < materials[i].size(); k++) {
out_material.push_back(materials[i][k]);
out_material[material_id_offset++] = materials[i][k];
}
}
// Copy attributes (3 floats per vertex, 3 floats per normal, 2 floats per texture-coordinate, 3 floats per color)
// You could also include a check here, if the sizes are dividable by 3 (resp. 2), but it's safe to simply assume, they do.
std::copy(attributes[i].vertices.begin(), attributes[i].vertices.end(), out_attribute.vertices.begin() + vertex_idx_offset * 3);
vertex_idx_offset += attributes[i].vertices.size() / 3;
std::copy(attributes[i].normals.begin(), attributes[i].normals.end(), out_attribute.normals.begin() + normal_idx_offset * 3);
normal_idx_offset += attributes[i].normals.size() / 3;
std::copy(attributes[i].texcoords.begin(), attributes[i].texcoords.end(), out_attribute.texcoords.begin() + texcoord_idx_offset * 2);
texcoord_idx_offset += attributes[i].texcoords.size() / 2;
std::copy(attributes[i].colors.begin(), attributes[i].colors.end(), out_attribute.colors.begin() + color_idx_offset);
color_idx_offset += attributes[i].colors.size();
}
}
int
main(
int argc,
char **argv)
int main(int argc, char **argv)
{
if (argc < 3) {
printf("Usage: obj_sticher input0.obj input1.obj ... output.obj\n");
@@ -78,16 +126,16 @@ main(
int num_objfiles = argc - 2;
std::string out_filename = std::string(argv[argc-1]); // last element
std::vector<Shape> shapes;
std::vector<Material> materials;
shapes.resize(num_objfiles);
materials.resize(num_objfiles);
std::vector<Attribute> attributes(num_objfiles);
std::vector<Shape> shapes(num_objfiles);
std::vector<Material> materials(num_objfiles);
for (int i = 0; i < num_objfiles; i++) {
std::cout << "Loading " << argv[i+1] << " ... " << std::flush;
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(shapes[i], materials[i], err, argv[i+1]);
bool ret = tinyobj::LoadObj(&attributes[i], &shapes[i], &materials[i], &warn, &err, argv[i+1]);
if (!err.empty()) {
std::cerr << err << std::endl;
}
@@ -98,12 +146,13 @@ main(
std::cout << "DONE." << std::endl;
}
std::vector<tinyobj::shape_t> out_shape;
std::vector<tinyobj::material_t> out_material;
StichObjs(out_shape, out_material, shapes, materials);
Attribute out_attribute;
Shape out_shape;
Material out_material;
StichObjs(out_attribute, out_shape, out_material, attributes, shapes, materials);
bool coordTransform = true;
bool ret = WriteObj(out_filename, out_shape, out_material, coordTransform);
bool ret = WriteObj(out_filename, out_attribute, out_shape, out_material, coordTransform);
assert(ret);
return 0;

View File

@@ -30,6 +30,8 @@ bool WriteMat(const std::string& filename, const std::vector<tinyobj::material_t
fprintf(fp, "Ke %f %f %f\n", mat.emission[0], mat.emission[1], mat.emission[2]);
fprintf(fp, "Ns %f\n", mat.shininess);
fprintf(fp, "Ni %f\n", mat.ior);
fprintf(fp, "illum %d\n", mat.illum);
fprintf(fp, "\n");
// @todo { texture }
}
@@ -38,7 +40,7 @@ bool WriteMat(const std::string& filename, const std::vector<tinyobj::material_t
return true;
}
bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform) {
bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform) {
FILE* fp = fopen(filename.c_str(), "w");
if (!fp) {
fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str());
@@ -48,17 +50,53 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
std::string basename = GetFileBasename(filename);
std::string material_filename = basename + ".mtl";
int v_offset = 0;
int vn_offset = 0;
int vt_offset = 0;
int prev_material_id = -1;
fprintf(fp, "mtllib %s\n", material_filename.c_str());
fprintf(fp, "mtllib %s\n\n", material_filename.c_str());
// facevarying vtx
for (size_t k = 0; k < attributes.vertices.size(); k+=3) {
if (coordTransform) {
fprintf(fp, "v %f %f %f\n",
attributes.vertices[k + 0],
attributes.vertices[k + 2],
-attributes.vertices[k + 1]);
} else {
fprintf(fp, "v %f %f %f\n",
attributes.vertices[k + 0],
attributes.vertices[k + 1],
attributes.vertices[k + 2]);
}
}
fprintf(fp, "\n");
// facevarying normal
for (size_t k = 0; k < attributes.normals.size(); k += 3) {
if (coordTransform) {
fprintf(fp, "vn %f %f %f\n",
attributes.normals[k + 0],
attributes.normals[k + 2],
-attributes.normals[k + 1]);
} else {
fprintf(fp, "vn %f %f %f\n",
attributes.normals[k + 0],
attributes.normals[k + 1],
attributes.normals[k + 2]);
}
}
fprintf(fp, "\n");
// facevarying texcoord
for (size_t k = 0; k < attributes.texcoords.size(); k += 2) {
fprintf(fp, "vt %f %f\n",
attributes.texcoords[k + 0],
attributes.texcoords[k + 1]);
}
for (size_t i = 0; i < shapes.size(); i++) {
bool has_vn = false;
bool has_vt = false;
fprintf(fp, "\n");
if (shapes[i].name.empty()) {
fprintf(fp, "g Unknown\n");
@@ -66,101 +104,53 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
fprintf(fp, "g %s\n", shapes[i].name.c_str());
}
//if (!shapes[i].material.name.empty()) {
// fprintf(fp, "usemtl %s\n", shapes[i].material.name.c_str());
//}
// facevarying vtx
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
for (int j = 0; j < 3; j++) {
int idx = shapes[i].mesh.indices[3*k+j];
if (coordTransform) {
fprintf(fp, "v %f %f %f\n",
shapes[i].mesh.positions[3*idx+0],
shapes[i].mesh.positions[3*idx+2],
-shapes[i].mesh.positions[3*idx+1]);
} else {
fprintf(fp, "v %f %f %f\n",
shapes[i].mesh.positions[3*idx+0],
shapes[i].mesh.positions[3*idx+1],
shapes[i].mesh.positions[3*idx+2]);
}
}
bool has_vn = false;
bool has_vt = false;
// Assumes normals and textures are set shape-wise.
if(shapes[i].mesh.indices.size() > 0){
has_vn = shapes[i].mesh.indices[0].normal_index != -1;
has_vt = shapes[i].mesh.indices[0].texcoord_index != -1;
}
// facevarying normal
if (shapes[i].mesh.normals.size() > 0) {
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
for (int j = 0; j < 3; j++) {
int idx = shapes[i].mesh.indices[3*k+j];
if (coordTransform) {
fprintf(fp, "vn %f %f %f\n",
shapes[i].mesh.normals[3*idx+0],
shapes[i].mesh.normals[3*idx+2],
-shapes[i].mesh.normals[3*idx+1]);
} else {
fprintf(fp, "vn %f %f %f\n",
shapes[i].mesh.normals[3*idx+0],
shapes[i].mesh.normals[3*idx+1],
shapes[i].mesh.normals[3*idx+2]);
}
}
}
}
if (shapes[i].mesh.normals.size() > 0) has_vn = true;
// facevarying texcoord
if (shapes[i].mesh.texcoords.size() > 0) {
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
for (int j = 0; j < 3; j++) {
int idx = shapes[i].mesh.indices[3*k+j];
fprintf(fp, "vt %f %f\n",
shapes[i].mesh.texcoords[2*idx+0],
shapes[i].mesh.texcoords[2*idx+1]);
}
}
}
if (shapes[i].mesh.texcoords.size() > 0) has_vt = true;
// face
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
// Face index is 1-base.
//int v0 = shapes[i].mesh.indices[3*k+0] + 1 + v_offset;
//int v1 = shapes[i].mesh.indices[3*k+1] + 1 + v_offset;
//int v2 = shapes[i].mesh.indices[3*k+2] + 1 + v_offset;
int v0 = (3*k + 0) + 1 + v_offset;
int v1 = (3*k + 1) + 1 + v_offset;
int v2 = (3*k + 2) + 1 + v_offset;
int vt0 = (3*k + 0) + 1 + vt_offset;
int vt1 = (3*k + 1) + 1 + vt_offset;
int vt2 = (3*k + 2) + 1 + vt_offset;
int material_id = shapes[i].mesh.material_ids[k];
int face_index = 0;
for (size_t k = 0; k < shapes[i].mesh.indices.size(); k += shapes[i].mesh.num_face_vertices[face_index++]) {
// Check Materials
int material_id = shapes[i].mesh.material_ids[face_index];
if (material_id != prev_material_id) {
std::string material_name = materials[material_id].name;
fprintf(fp, "usemtl %s\n", material_name.c_str());
prev_material_id = material_id;
}
if (has_vn && has_vt) {
fprintf(fp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
v0, vt0, v0, v1, vt1, v1, v2, vt2, v2);
} else if (has_vn && !has_vt) {
fprintf(fp, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2);
} else if (!has_vn && has_vt) {
fprintf(fp, "f %d/%d %d/%d %d/%d\n", v0, v0, v1, v1, v2, v2);
} else {
fprintf(fp, "f %d %d %d\n", v0, v1, v2);
unsigned char v_per_f = shapes[i].mesh.num_face_vertices[face_index];
// Imperformant, but if you want to have variable vertices per face, you need some kind of a dynamic loop.
fprintf(fp, "f");
for(int l = 0; l < v_per_f; l++){
const tinyobj::index_t& ref = shapes[i].mesh.indices[k + l];
if(has_vn && has_vt){
// v0/t0/vn0
fprintf(fp, " %d/%d/%d", ref.vertex_index + 1, ref.texcoord_index + 1, ref.normal_index + 1);
continue;
}
if(has_vn && !has_vt){
// v0//vn0
fprintf(fp, " %d//%d", ref.vertex_index + 1, ref.normal_index + 1);
continue;
}
if(!has_vn && has_vt){
// v0/vt0
fprintf(fp, " %d/%d", ref.vertex_index + 1, ref.texcoord_index + 1);
continue;
}
if(!has_vn && !has_vt){
// v0 v1 v2
fprintf(fp, " %d", ref.vertex_index + 1);
continue;
}
}
fprintf(fp, "\n");
}
v_offset += shapes[i].mesh.indices.size();
//vn_offset += shapes[i].mesh.normals.size() / 3;
vt_offset += shapes[i].mesh.texcoords.size() / 2;
}
fclose(fp);
@@ -172,5 +162,3 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
return ret;
}

View File

@@ -3,7 +3,6 @@
#include "../../tiny_obj_loader.h"
extern bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform = false);
extern bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform = false);
#endif // __OBJ_WRITER_H__

View File

@@ -37,11 +37,11 @@ extern "C" {
#include <windows.h>
#ifdef max
#undef max
#undef max
#endif
#ifdef min
#undef min
#undef min
#endif
#include <mmsystem.h>
@@ -143,15 +143,15 @@ float eye[3], lookat[3], up[3];
GLFWwindow* window;
static std::string GetBaseDir(const std::string &filepath) {
static std::string GetBaseDir(const std::string& filepath) {
if (filepath.find_last_of("/\\") != std::string::npos)
return filepath.substr(0, filepath.find_last_of("/\\"));
return "";
}
static bool FileExists(const std::string &abs_filename) {
static bool FileExists(const std::string& abs_filename) {
bool ret;
FILE *fp = fopen(abs_filename.c_str(), "rb");
FILE* fp = fopen(abs_filename.c_str(), "rb");
if (fp) {
ret = true;
fclose(fp);
@@ -191,14 +191,106 @@ static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
N[0] /= len;
N[1] /= len;
N[2] /= len;
}
}
namespace // Local utility functions
{
struct vec3 {
float v[3];
vec3() {
v[0] = 0.0f;
v[1] = 0.0f;
v[2] = 0.0f;
}
};
void normalizeVector(vec3 &v) {
float len2 = v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
v.v[0] /= len;
v.v[1] /= len;
v.v[2] /= len;
}
}
// Check if `mesh_t` contains smoothing group id.
bool hasSmoothingGroup(const tinyobj::shape_t& shape)
{
for (size_t i = 0; i < shape.mesh.smoothing_group_ids.size(); i++) {
if (shape.mesh.smoothing_group_ids[i] > 0) {
return true;
}
}
return false;
}
void computeSmoothingNormals(const tinyobj::attrib_t& attrib, const tinyobj::shape_t& shape,
std::map<int, vec3>& smoothVertexNormals) {
smoothVertexNormals.clear();
std::map<int, vec3>::iterator iter;
for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) {
// Get the three indexes of the face (all faces are triangular)
tinyobj::index_t idx0 = shape.mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shape.mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shape.mesh.indices[3 * f + 2];
// Get the three vertex indexes and coordinates
int vi[3]; // indexes
float v[3][3]; // coordinates
for (int k = 0; k < 3; k++) {
vi[0] = idx0.vertex_index;
vi[1] = idx1.vertex_index;
vi[2] = idx2.vertex_index;
assert(vi[0] >= 0);
assert(vi[1] >= 0);
assert(vi[2] >= 0);
v[0][k] = attrib.vertices[3 * vi[0] + k];
v[1][k] = attrib.vertices[3 * vi[1] + k];
v[2][k] = attrib.vertices[3 * vi[2] + k];
}
// Compute the normal of the face
float normal[3];
CalcNormal(normal, v[0], v[1], v[2]);
// Add the normal to the three vertexes
for (size_t i = 0; i < 3; ++i) {
iter = smoothVertexNormals.find(vi[i]);
if (iter != smoothVertexNormals.end()) {
// add
iter->second.v[0] += normal[0];
iter->second.v[1] += normal[1];
iter->second.v[2] += normal[2];
} else {
smoothVertexNormals[vi[i]].v[0] = normal[0];
smoothVertexNormals[vi[i]].v[1] = normal[1];
smoothVertexNormals[vi[i]].v[2] = normal[2];
}
}
} // f
// Normalize the normals, that is, make them unit vectors
for (iter = smoothVertexNormals.begin(); iter != smoothVertexNormals.end();
iter++) {
normalizeVector(iter->second);
}
} // computeSmoothingNormals
} // namespace
static bool LoadObjAndConvert(float bmin[3], float bmax[3],
std::vector<DrawObject>* drawObjects,
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures,
const char* filename) {
std::vector<DrawObject>* drawObjects,
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures,
const char* filename) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
@@ -216,9 +308,13 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
base_dir += "/";
#endif
std::string warn;
std::string err;
bool ret =
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, base_dir.c_str());
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename,
base_dir.c_str());
if (!warn.empty()) {
std::cout << "WARN: " << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;
}
@@ -242,56 +338,62 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
materials.push_back(tinyobj::material_t());
for (size_t i = 0; i < materials.size(); i++) {
printf("material[%d].diffuse_texname = %s\n", int(i), materials[i].diffuse_texname.c_str());
printf("material[%d].diffuse_texname = %s\n", int(i),
materials[i].diffuse_texname.c_str());
}
// Load diffuse textures
{
for (size_t m = 0; m < materials.size(); m++) {
tinyobj::material_t* mp = &materials[m];
for (size_t m = 0; m < materials.size(); m++) {
tinyobj::material_t* mp = &materials[m];
if (mp->diffuse_texname.length() > 0) {
// Only load the texture if it is not already loaded
if (textures.find(mp->diffuse_texname) == textures.end()) {
GLuint texture_id;
int w, h;
int comp;
if (mp->diffuse_texname.length() > 0) {
// Only load the texture if it is not already loaded
if (textures.find(mp->diffuse_texname) == textures.end()) {
GLuint texture_id;
int w, h;
int comp;
std::string texture_filename = mp->diffuse_texname;
if (!FileExists(texture_filename)) {
// Append base dir.
texture_filename = base_dir + mp->diffuse_texname;
if (!FileExists(texture_filename)) {
std::cerr << "Unable to find file: " << mp->diffuse_texname << std::endl;
exit(1);
}
}
unsigned char* image = stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (!image) {
std::cerr << "Unable to load texture: " << texture_filename << std::endl;
exit(1);
}
std::cout << "Loaded texture: " << texture_filename << ", w = " << w << ", h = " << h << ", comp = " << comp << std::endl;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (comp == 3) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
}
else if (comp == 4) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
} else {
assert(0); // TODO
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
std::string texture_filename = mp->diffuse_texname;
if (!FileExists(texture_filename)) {
// Append base dir.
texture_filename = base_dir + mp->diffuse_texname;
if (!FileExists(texture_filename)) {
std::cerr << "Unable to find file: " << mp->diffuse_texname
<< std::endl;
exit(1);
}
}
unsigned char* image =
stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (!image) {
std::cerr << "Unable to load texture: " << texture_filename
<< std::endl;
exit(1);
}
std::cout << "Loaded texture: " << texture_filename << ", w = " << w
<< ", h = " << h << ", comp = " << comp << std::endl;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (comp == 3) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_BYTE, image);
} else if (comp == 4) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
} else {
assert(0); // TODO
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
}
}
}
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
@@ -301,6 +403,14 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
for (size_t s = 0; s < shapes.size(); s++) {
DrawObject o;
std::vector<float> buffer; // pos(3float), normal(3float), color(3float)
// Check for smoothing group and compute smoothing normals
std::map<int, vec3> smoothVertexNormals;
if (hasSmoothingGroup(shapes[s]) > 0) {
std::cout << "Compute smoothingNormal for shape [" << s << "]" << std::endl;
computeSmoothingNormals(attrib, shapes[s], smoothVertexNormals);
}
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) {
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
@@ -308,23 +418,40 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
int current_material_id = shapes[s].mesh.material_ids[f];
if ((current_material_id < 0) || (current_material_id >= static_cast<int>(materials.size()))) {
if ((current_material_id < 0) ||
(current_material_id >= static_cast<int>(materials.size()))) {
// Invaid material ID. Use default material.
current_material_id = materials.size() - 1; // Default material is added to the last item in `materials`.
current_material_id =
materials.size() -
1; // Default material is added to the last item in `materials`.
}
//if (current_material_id >= materials.size()) {
// std::cerr << "Invalid material index: " << current_material_id << std::endl;
// if (current_material_id >= materials.size()) {
// std::cerr << "Invalid material index: " << current_material_id <<
// std::endl;
//}
//
float diffuse[3];
for (size_t i = 0; i < 3; i++) {
diffuse[i] = materials[current_material_id].diffuse[i];
diffuse[i] = materials[current_material_id].diffuse[i];
}
float tc[3][2];
if (attrib.texcoords.size() > 0) {
assert(attrib.texcoords.size() > 2 * idx0.texcoord_index + 1);
assert(attrib.texcoords.size() > 2 * idx1.texcoord_index + 1);
assert(attrib.texcoords.size() > 2 * idx2.texcoord_index + 1);
if ((idx0.texcoord_index < 0) || (idx1.texcoord_index < 0) ||
(idx2.texcoord_index < 0)) {
// face does not contain valid uv index.
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
} else {
assert(attrib.texcoords.size() >
size_t(2 * idx0.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx1.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx2.texcoord_index + 1));
// Flip Y coord.
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
@@ -333,13 +460,14 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1];
tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index];
tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1];
}
} else {
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
}
float v[3][3];
@@ -363,27 +491,63 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
}
float n[3][3];
if (attrib.normals.size() > 0) {
int f0 = idx0.normal_index;
int f1 = idx1.normal_index;
int f2 = idx2.normal_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3 * f0 + k];
n[1][k] = attrib.normals[3 * f1 + k];
n[2][k] = attrib.normals[3 * f2 + k];
{
bool invalid_normal_index = false;
if (attrib.normals.size() > 0) {
int nf0 = idx0.normal_index;
int nf1 = idx1.normal_index;
int nf2 = idx2.normal_index;
if ((nf0 < 0) || (nf1 < 0) || (nf2 < 0)) {
// normal index is missing from this face.
invalid_normal_index = true;
} else {
for (int k = 0; k < 3; k++) {
assert(size_t(3 * nf0 + k) < attrib.normals.size());
assert(size_t(3 * nf1 + k) < attrib.normals.size());
assert(size_t(3 * nf2 + k) < attrib.normals.size());
n[0][k] = attrib.normals[3 * nf0 + k];
n[1][k] = attrib.normals[3 * nf1 + k];
n[2][k] = attrib.normals[3 * nf2 + k];
}
}
} else {
invalid_normal_index = true;
}
if (invalid_normal_index && !smoothVertexNormals.empty()) {
// Use smoothing normals
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
if (f0 >= 0 && f1 >= 0 && f2 >= 0) {
n[0][0] = smoothVertexNormals[f0].v[0];
n[0][1] = smoothVertexNormals[f0].v[1];
n[0][2] = smoothVertexNormals[f0].v[2];
n[1][0] = smoothVertexNormals[f1].v[0];
n[1][1] = smoothVertexNormals[f1].v[1];
n[1][2] = smoothVertexNormals[f1].v[2];
n[2][0] = smoothVertexNormals[f2].v[0];
n[2][1] = smoothVertexNormals[f2].v[1];
n[2][2] = smoothVertexNormals[f2].v[2];
invalid_normal_index = false;
}
}
if (invalid_normal_index) {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
}
} else {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
}
for (int k = 0; k < 3; k++) {
@@ -396,11 +560,9 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
// Combine normal and diffuse to get color.
float normal_factor = 0.2;
float diffuse_factor = 1 - normal_factor;
float c[3] = {
n[k][0] * normal_factor + diffuse[0] * diffuse_factor,
n[k][1] * normal_factor + diffuse[1] * diffuse_factor,
n[k][2] * normal_factor + diffuse[2] * diffuse_factor
};
float c[3] = {n[k][0] * normal_factor + diffuse[0] * diffuse_factor,
n[k][1] * normal_factor + diffuse[1] * diffuse_factor,
n[k][2] * normal_factor + diffuse[2] * diffuse_factor};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
@@ -422,19 +584,22 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
o.numTriangles = 0;
// OpenGL viewer does not support texturing with per-face material.
if (shapes[s].mesh.material_ids.size() > 0 && shapes[s].mesh.material_ids.size() > s) {
o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID of the first face.
if (shapes[s].mesh.material_ids.size() > 0 &&
shapes[s].mesh.material_ids.size() > s) {
o.material_id = shapes[s].mesh.material_ids[0]; // use the material ID
// of the first face.
} else {
o.material_id = materials.size() - 1; // = ID for default material.
o.material_id = materials.size() - 1; // = ID for default material.
}
printf("shape[%d] material_id %d\n", int(s), int(o.material_id));
if (buffer.size() > 0) {
glGenBuffers(1, &o.vb_id);
glBindBuffer(GL_ARRAY_BUFFER, o.vb_id);
glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float), &buffer.at(0),
GL_STATIC_DRAW);
o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) / 3; // 3:vtx, 3:normal, 3:col, 2:texcoord
glBufferData(GL_ARRAY_BUFFER, buffer.size() * sizeof(float),
&buffer.at(0), GL_STATIC_DRAW);
o.numTriangles = buffer.size() / (3 + 3 + 3 + 2) /
3; // 3:vtx, 3:normal, 3:col, 2:texcoord
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
@@ -467,7 +632,7 @@ static void reshapeFunc(GLFWwindow* window, int w, int h) {
}
static void keyboardFunc(GLFWwindow* window, int key, int scancode, int action,
int mods) {
int mods) {
(void)window;
(void)scancode;
(void)mods;
@@ -549,7 +714,9 @@ static void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
prevMouseY = mouse_y;
}
static void Draw(const std::vector<DrawObject>& drawObjects, std::vector<tinyobj::material_t>& materials, std::map<std::string, GLuint>& textures) {
static void Draw(const std::vector<DrawObject>& drawObjects,
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures) {
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
@@ -572,7 +739,7 @@ static void Draw(const std::vector<DrawObject>& drawObjects, std::vector<tinyobj
if ((o.material_id < materials.size())) {
std::string diffuse_texname = materials[o.material_id].diffuse_texname;
if (textures.find(diffuse_texname) != textures.end()) {
glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]);
glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]);
}
}
glVertexPointer(3, GL_FLOAT, stride, (const void*)0);
@@ -668,7 +835,8 @@ int main(int argc, char** argv) {
float bmin[3], bmax[3];
std::vector<tinyobj::material_t> materials;
std::map<std::string, GLuint> textures;
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures, argv[1])) {
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures,
argv[1])) {
return -1;
}

View File

@@ -9,8 +9,9 @@ bool Voxelize(const char* filename, float voxelsizex, float voxelsizey, float vo
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename);
if (!err.empty()) {
printf("err: %s\n", err.c_str());

View File

@@ -6,11 +6,21 @@
* C++-11 compiler
## How to build
```
$ premak5 gmake
```
## Compile options
* zstd compressed .obj support. `--with-zstd` premake option.
* gzip compressed .obj support. `--with-zlib` premake option.
## Notes on AMD GPU + Linux
You may need to link with libdrm(`-ldrm`).
## Licenses
* lfpAlloc : MIT license.

View File

@@ -312,6 +312,10 @@ typedef struct {
typedef struct {
std::string name; // group name or object name.
// Shape's corresponding faces are accessed by attrib.indices[face_offset,
// face_offset + length] NOTE: you'll need to sum up
// attrib.face_num_verts[face_offset, face_offset + length] to find actual
// number of faces.
unsigned int face_offset;
unsigned int length;
} shape_t;
@@ -330,7 +334,14 @@ typedef struct {
std::vector<float, lfpAlloc::lfpAllocator<float> > normals;
std::vector<float, lfpAlloc::lfpAllocator<float> > texcoords;
std::vector<index_t, lfpAlloc::lfpAllocator<index_t> > indices;
// # of vertices for each face.
// 3 for triangle, 4 for qual, ...
// If triangulation is enabled and the original face are quad,
// face_num_verts will be 6(3 + 3)
std::vector<int, lfpAlloc::lfpAllocator<int> > face_num_verts;
// Per-face material IDs.
std::vector<int, lfpAlloc::lfpAllocator<int> > material_ids;
} attrib_t;
@@ -594,19 +605,9 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
}
assemble :
{
// = pow(5.0, exponent);
double a = 1.0;
for (int i = 0; i < exponent; i++) {
a = a * 5.0;
}
*result =
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
(sign == '+' ? 1 : -1) * (mantissa * a) *
static_cast<double>(1ULL << exponent); // 5.0^exponent * 2^exponent
}
*result = (sign == '+' ? 1 : -1) *
(exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
: mantissa);
return true;
fail:
return false;
@@ -1046,7 +1047,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<material_t> *materials, const char *buf, size_t len,
const LoadOption &option);
} // namespace tinyobj_opt
} // namespace tinyobj_opt
#endif // TINOBJ_LOADER_OPT_H_
@@ -1238,6 +1239,8 @@ typedef struct {
// 3. Do parallel parsing for each line.
// 4. Reconstruct final mesh data structure.
// Raise # of max threads if you have more CPU cores...
// In 2018, 32 cores are getting common in high-end workstaion PC.
#define kMaxThreads (32)
static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
@@ -1306,15 +1309,19 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
end_idx = len - 1;
}
// true if the line currently read must be added to the current line
// info
bool new_line_found =
(t == 0) || is_line_ending(buf, start_idx - 1, end_idx);
size_t prev_pos = start_idx;
for (size_t i = start_idx; i < end_idx; i++) {
if (is_line_ending(buf, i, end_idx)) {
if ((t > 0) && (prev_pos == start_idx) &&
(!is_line_ending(buf, start_idx - 1, end_idx))) {
if (!new_line_found) {
// first linebreak found in (chunk > 0), and a line before this
// linebreak belongs to previous chunk, so skip it.
prev_pos = i + 1;
continue;
new_line_found = true;
} else {
LineInfo info;
info.pos = prev_pos;
@@ -1329,11 +1336,11 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
}
// Find extra line which spand across chunk boundary.
if ((t < num_threads) && (buf[end_idx - 1] != '\n')) {
auto extra_span_idx = (std::min)(end_idx - 1 + chunk_size, len);
for (size_t i = end_idx; i < extra_span_idx; i++) {
if (is_line_ending(buf, i, extra_span_idx)) {
// If at least one line started in this chunk, find where it ends in the
// rest of the buffer
if (new_line_found && (t < num_threads) && (buf[end_idx - 1] != '\n')) {
for (size_t i = end_idx; i < len; i++) {
if (is_line_ending(buf, i, len)) {
LineInfo info;
info.pos = prev_pos;
info.len = i - prev_pos;
@@ -1390,7 +1397,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
for (size_t t = 0; t < num_threads; t++) {
workers->push_back(std::thread([&, t]() {
for (size_t i = 0; i < line_infos[t].size(); i++) {
Command command;
bool ret = parseLine(&command, &buf[line_infos[t][i].pos],
@@ -1408,14 +1414,14 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
if (command.type == COMMAND_MTLLIB) {
// Save the indices of the `mtllib` command in `commands` to easily find it later
mtllib_t_index = t;
mtllib_i_index = commands->size();
mtllib_i_index = commands[t].size();
}
commands[t].emplace_back(std::move(command));
}
}
}));
}
@@ -1440,7 +1446,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// std::cout << "mtllib :" << material_filename << std::endl;
auto t1 = std::chrono::high_resolution_clock::now();
if (material_filename.back() == '\r') {
material_filename.pop_back();
}
std::ifstream ifs(material_filename);
if (ifs.good()) {
LoadMtl(&material_map, materials, &ifs);
@@ -1491,7 +1499,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
attrib->texcoords.resize(num_vt * 2);
attrib->indices.resize(num_f);
attrib->face_num_verts.resize(num_indices);
attrib->material_ids.resize(num_indices);
attrib->material_ids.resize(num_indices, -1);
size_t v_offsets[kMaxThreads];
size_t n_offsets[kMaxThreads];
@@ -1516,7 +1524,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
StackVector<std::thread, 16> workers;
for (size_t t = 0; t < num_threads; t++) {
int material_id = -1; // -1 = default unknown material.
workers->push_back(std::thread([&, t]() {
size_t v_count = v_offsets[t];
size_t n_count = n_offsets[t];
@@ -1529,15 +1536,40 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
continue;
} else if (commands[t][i].type == COMMAND_USEMTL) {
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;
commands[t][i].material_name_len > 0 &&
// check if there are still faces after this command
face_count < num_indices) {
// Find next face
bool found = false;
size_t i_start = i + 1, t_next, i_next;
for (t_next = t; t_next < num_threads; t_next++) {
for (i_next = i_start; i_next < commands[t_next].size();
i_next++) {
if (commands[t_next][i_next].type == COMMAND_F) {
found = true;
break;
}
}
if (found) break;
i_start = 0;
}
// Assign material to this face
if (found) {
std::string material_name(commands[t][i].material_name,
commands[t][i].material_name_len);
for (size_t k = 0;
k < commands[t_next][i_next].f_num_verts.size(); k++) {
if (material_map.find(material_name) != material_map.end()) {
attrib->material_ids[face_count + k] =
material_map[material_name];
} else {
// Assign invalid material ID
// Set a different value than the default, to
// prevent following faces from being assigned a valid
// material
attrib->material_ids[face_count + k] = -2;
}
}
}
}
} else if (commands[t][i].type == COMMAND_V) {
@@ -1564,7 +1596,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
index_t(vertex_index, texcoord_index, normal_index);
}
for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) {
attrib->material_ids[face_count + k] = material_id;
attrib->face_num_verts[face_count + k] =
commands[t][i].f_num_verts[k];
}
@@ -1580,6 +1611,12 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
workers[t].join();
}
// To each face with uninitialized material id,
// assign the material id of the last face preceding it that has one
for (size_t face_count = 1; face_count < num_indices; ++face_count)
if (attrib->material_ids[face_count] == -1)
attrib->material_ids[face_count] = attrib->material_ids[face_count - 1];
auto t_end = std::chrono::high_resolution_clock::now();
ms_merge = t_end - t_start;
}
@@ -1639,7 +1676,8 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
}
if (commands[t][i].type == COMMAND_F) {
face_count++;
// Consider generation of multiple faces per `f` line by triangulation
face_count += commands[t][i].f_num_verts.size();
}
}
}

View File

@@ -357,7 +357,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
size_t face_offset = 0;
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face.
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face(multiple of 3).
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0];
tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1];

BIN
fuzzer/afl.tar.gz Normal file

Binary file not shown.

20
fuzzer/runner.py Normal file
View File

@@ -0,0 +1,20 @@
import os, sys
import glob
import subprocess
def main():
for g in glob.glob("../tests/afl/id*"):
print(g)
cmd = ["../a.out", g]
proc = subprocess.Popen(cmd)
try:
outs, errs = proc.communicate(timeout=15)
print(outs)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
main()

View File

@@ -29,6 +29,13 @@ extern "C" {
#endif
#endif
#ifdef __clang__
#pragma clang diagnostic push
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
class timerutil {
public:
#ifdef _WIN32
@@ -130,14 +137,19 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
for (size_t i = 0; i < shapes.size(); i++) {
printf("shape[%ld].name = %s\n", static_cast<long>(i),
shapes[i].name.c_str());
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i),
printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.indices.size()));
printf("Size of shape[%ld].path.indices: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].path.indices.size()));
size_t index_offset = 0;
assert(shapes[i].mesh.num_face_vertices.size() ==
shapes[i].mesh.material_ids.size());
assert(shapes[i].mesh.num_face_vertices.size() ==
shapes[i].mesh.smoothing_group_ids.size());
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
@@ -158,6 +170,8 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
printf(" face[%ld].material_id = %d\n", static_cast<long>(f),
shapes[i].mesh.material_ids[f]);
printf(" face[%ld].smoothing_group_id = %d\n", static_cast<long>(f),
shapes[i].mesh.smoothing_group_ids[f]);
index_offset += fnum;
}
@@ -271,14 +285,19 @@ static bool TestLoadObj(const char* filename, const char* basepath = NULL,
timerutil t;
t.start();
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename,
basepath, triangulate);
t.end();
printf("Parsing time: %lu [msecs]\n", t.msec());
if (!warn.empty()) {
std::cout << "WARN: " << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;
std::cerr << "ERR: " << err << std::endl;
}
if (!ret) {
@@ -361,17 +380,13 @@ static bool TestStreamLoadObj() {
virtual ~MaterialStringStreamReader() {}
virtual bool operator()(const std::string& matId,
std::vector<material_t>* materials,
std::map<unsigned int, int>* matMap,
std::map<std::string, int>* matMap,
std::string* warn,
std::string* err) {
(void)err;
(void)matId;
std::string warning;
LoadMtl(matMap, materials, &m_matSStream, &warning);
LoadMtl(matMap, materials, &m_matSStream, warn, err);
if (!warning.empty()) {
if (err) {
(*err) += warning;
}
}
return true;
}
@@ -383,8 +398,9 @@ static bool TestStreamLoadObj() {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream,
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, &objStream,
&matSSReader);
if (!err.empty()) {

View File

@@ -0,0 +1,9 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
map_Kd -colorspace sRGB -o 0.1 diffuse.jpg
map_Ks -s 0.1 0.2 specular.jpg
map_bump -colorspace linear -bm 3 bumpmap.jpg

View File

@@ -0,0 +1,7 @@
mtllib colorspace-issue-184.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

@@ -13,7 +13,8 @@ v 2.000000 2.000000 0.000000
g front cube
usemtl white
f 1 2 3 4
g back cube
# two white spaces between 'back' and 'cube'
g back cube
# expects white material
f 8 7 6 5
g right cube

View File

@@ -0,0 +1,24 @@
newmtl white
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
newmtl red
Ka 0 0 0
Kd 1 0 0
Ks 0 0 0
newmtl green
Ka 0 0 0
Kd 0 1 0
Ks 0 0 0
newmtl blue
Ka 0 0 0
Kd 0 0 1
Ks 0 0 0
newmtl light
Ka 20 20 20
Kd 1 1 1
Ks 0 0 0

View File

@@ -0,0 +1,18 @@
mtllib invalid-face-definition.mtl
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
# 8 vertices
g front cube
usemtl white
f 1
g back cube
# expects white material
f 8 7

View File

@@ -0,0 +1,37 @@
o cube
mtllib cube.mtl
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
g cube
usemtl cube
s 1
f 1/1 2/2 3/3
f 3/3 2/2 4/4
s 2
f 3/1 4/2 5/3
f 5/3 4/2 6/4
s 3
f 5/4 6/3 7/2
f 7/2 6/3 8/1
s 4
f 7/1 8/2 1/3
f 1/3 8/2 2/4
s 5
f 2/1 8/2 4/3
f 4/3 8/2 6/4
s 6
f 7/1 1/2 5/3
f 5 1 3

View File

@@ -0,0 +1,23 @@
newmtl test1
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.5 0.2 0.2
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl test2
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.2 0.5 0.2
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000

View File

@@ -0,0 +1,51 @@
# cube.obj
#
mtllib issue-162-smoothing-group.mtl
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
usemtl test1
g test1
s 1
f 1/1/1 2/2/1 3/3/1
f 3/3/1 2/2/1 4/4/1
usemtl test2
g test2
s off
f 3/1/2 4/2/2 5/3/2
f 5/3/2 4/2/2 6/4/2
s 3
f 5/4/3 6/3/3 7/2/3
f 7/2/3 6/3/3 8/1/3
s 4
f 7/1/4 8/2/4 1/3/4
f 1/3/4 8/2/4 2/4/4
s 0
f 2/1/5 8/2/5 4/3/5
f 4/3/5 8/2/5 6/4/5
s 6
f 7/1/6 1/2/6 5/3/6
f 5/3/6 1/2/6 3/4/6

16
models/line-prim.obj Normal file
View File

@@ -0,0 +1,16 @@
mtllib cube.mtl
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
# 8 vertices
g g0
usemtl white
l 1 2 3 4
l 5 6 7

View File

@@ -0,0 +1,22 @@
# from tinyobjloader issue #29
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
v 4.000000 0.000000 -3.255298
v 4.000000 2.000000 -3.255298
#vn 0.000000 0.000000 1.000000
#vn 0.000000 0.000000 1.000000
#vn 0.276597 0.000000 0.960986
#vn 0.276597 0.000000 0.960986
#vn 0.531611 0.000000 0.846988
#vn 0.531611 0.000000 0.846988
# 6 vertices
# 6 normals
g all
s 1
f 1//1 2//2 3//3 4//4
f 4//4 3//3 5//5

View File

@@ -0,0 +1,24 @@
newmtl white
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
newmtl red
Ka 0 0 0
Kd 1 0 0
Ks 0 0 0
newmtl green
Ka 0 0 0
Kd 0 1 0
Ks 0 0 0
newmtl blue
Ka 0 0 0
Kd 0 0 1
Ks 0 0 0
newmtl light
Ka 20 20 20
Kd 1 1 1
Ks 0 0 0

View File

@@ -0,0 +1,35 @@
mtllib smoothing-normal.mtl
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
# 8 vertices
g front cube
usemtl white
s 1
f 1 2 3 4
#g bottom cube
#usemtl white
s 1
f 2 6 7 3
g back cube
# expects white material
s off
f 8 7 6 5
#g right cube
#usemtl red
#f 4 3 7 8
#g top cube
#usemtl white
#f 5 1 4 8
#g left cube
#usemtl green
#f 5 6 2 1
# 6 elements

View File

@@ -18,11 +18,9 @@ typedef std::vector<int> vecti;
PyObject* pyTupleFromfloat3(float array[3]) {
int i;
PyObject* tuple = PyTuple_New(3);
for (i = 0; i <= 2; i++) {
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
}
return tuple;
}
@@ -30,32 +28,27 @@ extern "C" {
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj;
char const* current_name;
char const* filename;
vectd vect;
std::vector<tinyobj::index_t> indices;
std::vector<unsigned char> face_verts;
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
std::string err;
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
std::string err, warn;
tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename);
pyshapes = PyDict_New();
pymaterials = PyDict_New();
pymaterial_indices = PyList_New(0);
rtndict = PyDict_New();
attribobj = PyDict_New();
for (int i = 0; i <= 2; i++) {
current = PyList_New(0);
switch (i) {
case 0:
current_name = "vertices";
@@ -72,124 +65,137 @@ static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
}
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
PyObject* value = PyFloat_FromDouble(*it);
PyList_Insert(current, it - vect.begin(), value);
Py_DECREF(value);
}
PyDict_SetItemString(attribobj, current_name, current);
Py_DECREF(current);
}
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
shape != shapes.end(); shape++) {
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin(); shape != shapes.end(); shape++) {
meshobj = PyDict_New();
tinyobj::mesh_t cm = (*shape).mesh;
{
current = PyList_New(0);
for (size_t i = 0; i < cm.indices.size(); i++) {
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
// ...
PyList_Insert(current, 3 * i + 0,
PyLong_FromLong(cm.indices[i].vertex_index));
PyList_Insert(current, 3 * i + 1,
PyLong_FromLong(cm.indices[i].normal_index));
PyList_Insert(current, 3 * i + 2,
PyLong_FromLong(cm.indices[i].texcoord_index));
PyObject* value = PyLong_FromLong(cm.indices[i].vertex_index);
PyList_Insert(current, 3 * i + 0, value);
Py_DECREF(value);
value = PyLong_FromLong(cm.indices[i].normal_index);
PyList_Insert(current, 3 * i + 1, value);
Py_DECREF(value);
value = PyLong_FromLong(cm.indices[i].texcoord_index);
PyList_Insert(current, 3 * i + 2, value);
Py_DECREF(value);
}
PyDict_SetItemString(meshobj, "indices", current);
Py_DECREF(current);
}
{
current = PyList_New(0);
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
// Widen data type to long.
PyList_Insert(current, i, PyLong_FromLong(cm.num_face_vertices[i]));
PyObject* value = PyLong_FromLong(cm.num_face_vertices[i]);
PyList_Insert(current, i, value);
Py_DECREF(value);
}
PyDict_SetItemString(meshobj, "num_face_vertices", current);
Py_DECREF(current);
}
{
current = PyList_New(0);
for (size_t i = 0; i < cm.material_ids.size(); i++) {
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i]));
PyObject* value = PyLong_FromLong(cm.material_ids[i]);
PyList_Insert(current, i, value);
Py_DECREF(value);
}
PyDict_SetItemString(meshobj, "material_ids", current);
Py_DECREF(current);
}
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
Py_DECREF(meshobj);
}
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
mat != materials.end(); mat++) {
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin(); mat != materials.end(); mat++) {
PyObject* matobj = PyDict_New();
PyObject* unknown_parameter = PyDict_New();
for (std::map<std::string, std::string>::iterator p =
mat->unknown_parameter.begin();
p != mat->unknown_parameter.end(); ++p) {
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
PyUnicode_FromString(p->second.c_str()));
for (std::map<std::string, std::string>::iterator p = mat->unknown_parameter.begin(); p != mat->unknown_parameter.end(); ++p) {
PyObject* value = PyUnicode_FromString(p->second.c_str());
PyDict_SetItemString(unknown_parameter, p->first.c_str(), value);
Py_DECREF(value);
}
PyDict_SetItemString(matobj, "shininess",
PyFloat_FromDouble(mat->shininess));
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble(mat->ior));
PyDict_SetItemString(matobj, "dissolve",
PyFloat_FromDouble(mat->dissolve));
PyDict_SetItemString(matobj, "illum", PyLong_FromLong(mat->illum));
PyDict_SetItemString(matobj, "ambient_texname",
PyUnicode_FromString(mat->ambient_texname.c_str()));
PyDict_SetItemString(matobj, "diffuse_texname",
PyUnicode_FromString(mat->diffuse_texname.c_str()));
PyDict_SetItemString(matobj, "specular_texname",
PyUnicode_FromString(mat->specular_texname.c_str()));
PyDict_SetItemString(
matobj, "specular_highlight_texname",
PyUnicode_FromString(mat->specular_highlight_texname.c_str()));
PyDict_SetItemString(matobj, "bump_texname",
PyUnicode_FromString(mat->bump_texname.c_str()));
PyDict_SetItemString(
matobj, "displacement_texname",
PyUnicode_FromString(mat->displacement_texname.c_str()));
PyDict_SetItemString(matobj, "alpha_texname",
PyUnicode_FromString(mat->alpha_texname.c_str()));
PyObject* value = PyFloat_FromDouble(mat->shininess);
PyDict_SetItemString(matobj, "shininess", value);
Py_DECREF(value);
value = PyFloat_FromDouble(mat->ior);
PyDict_SetItemString(matobj, "ior", value);
Py_DECREF(value);
value = PyFloat_FromDouble(mat->dissolve);
PyDict_SetItemString(matobj, "dissolve", value);
Py_DECREF(value);
value = PyLong_FromLong(mat->illum);
PyDict_SetItemString(matobj, "illum", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->ambient_texname.c_str());
PyDict_SetItemString(matobj, "ambient_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->diffuse_texname.c_str());
PyDict_SetItemString(matobj, "diffuse_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->specular_texname.c_str());
PyDict_SetItemString(matobj, "specular_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->specular_highlight_texname.c_str());
PyDict_SetItemString(matobj, "specular_highlight_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->bump_texname.c_str());
PyDict_SetItemString(matobj, "bump_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->displacement_texname.c_str());
PyDict_SetItemString(matobj, "displacement_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->alpha_texname.c_str());
PyDict_SetItemString(matobj, "alpha_texname", value);
Py_DECREF(value);
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
PyDict_SetItemString(matobj, "specular",
pyTupleFromfloat3(mat->specular));
PyDict_SetItemString(matobj, "transmittance",
pyTupleFromfloat3(mat->transmittance));
PyDict_SetItemString(matobj, "emission",
pyTupleFromfloat3(mat->emission));
PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3(mat->specular));
PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3(mat->transmittance));
PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3(mat->emission));
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
Py_DECREF(unknown_parameter);
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
PyList_Append(pymaterial_indices, PyUnicode_FromString(mat->name.c_str()));
Py_DECREF(matobj);
value = PyUnicode_FromString(mat->name.c_str());
PyList_Append(pymaterial_indices, value);
Py_DECREF(value);
}
PyDict_SetItemString(rtndict, "shapes", pyshapes);
Py_DECREF(pyshapes);
PyDict_SetItemString(rtndict, "materials", pymaterials);
Py_DECREF(pymaterials);
PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices);
Py_DECREF(pymaterial_indices);
PyDict_SetItemString(rtndict, "attribs", attribobj);
Py_DECREF(attribobj);
return rtndict;
}
static PyMethodDef mMethods[] = {
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
NULL, -1, mMethods};
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader", NULL, -1, mMethods};
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
return PyModule_Create(&moduledef);

24
tests/issue-177.mtl Normal file
View File

@@ -0,0 +1,24 @@
newmtl white
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
newmtl red
Ka 0 0 0
Kd 1 0 0
Ks 0 0 0
newmtl green
Ka 0 0 0
Kd 0 1 0
Ks 0 0 0
newmtl blue
Ka 0 0 0
Kd 0 0 1
Ks 0 0 0
newmtl light
Ka 20 20 20
Kd 1 1 1
Ks 0 0 0

31
tests/issue-177.obj Normal file
View File

@@ -0,0 +1,31 @@
mtllib issue-177.mtl
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
# 8 vertices
g front cube
usemtl white
f 1 2 3 4
g back cube
# expects white material
f 8 7 6 5
g right cube
usemtl red
f 4 3 7 8
g top cube
usemtl white
f 5 1 4 8
g left cube
usemtl green
f 5 6 2 1
g bottom cube
usemtl white
f 2 6 7 3
# 6 elements

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff