98 Commits
c89 ... texopt

Author SHA1 Message Date
Syoyo Fujita
0948ca0417 Add more support for parsing texture options.
Add more unit testing for texture options.
2016-11-03 02:58:44 +09:00
Syoyo Fujita
582eb2b818 Initial support of texture options. 2016-11-02 02:11:17 +09:00
Syoyo Fujita
c2474e27ab Fix seg fault when no material assigned to object in viewer example.
Bump version v1.0.2.
2016-10-24 23:58:33 +09:00
Syoyo Fujita
6c6390f034 Fix typo. 2016-10-24 23:58:23 +09:00
Syoyo Fujita
4d6649cc6d Suppress compiler warnings. 2016-10-24 23:54:20 +09:00
Syoyo Fujita
d543b1447f Merge pull request #105 from dotdash/speedup
Improve parsing speed by about a factor of 2 for large files
2016-10-24 23:37:34 +09:00
Björn Steinbrink
d6eeb14216 Avoid unnecessary ldexp() and pow() calls
Parse times for some large .obj files (without asan):

         File A  File B  File C
Before   1239ms   294ms   271ms
After    1037ms   203ms   190ms
2016-10-24 14:47:59 +02:00
Björn Steinbrink
aa670fe91e Use a lookup table to speed up float parsing
The pow() function is pretty expensive, so creating a small lookup
table for the first few negative powers of ten provides a big speedup.

Parse times for some large .obj files (without asan):

        File A  File B  File C
Before  2500ms   573ms   545ms
After   1239ms   294ms   271ms
2016-10-24 12:15:42 +02:00
Björn Steinbrink
ebdbd8a231 Avoid unnecessary reallocations of the "linebuf" string
safeGetline() already clears the string buffer before writing to it, so
the same buffer can be used multiple times, but the loops calling
safeGetline() have the string scoped within the loop, so its
constructed and destructed in each loop iteration, causing lots of
unnecessary allocations.

Parse times for some large .obj files (without asan):

        File A  File B  File C
Before  2743ms   589ms   615ms
After   2500ms   573ms   545ms
2016-10-24 12:13:56 +02:00
Syoyo Fujita
fed4322d26 Merge branch 'master' of github.com:syoyo/tinyobjloader 2016-10-18 17:34:15 +09:00
Syoyo Fujita
039d4a6c54 Fix a shape is lost if obj ends with a 'usemtl'. Fixes #104 2016-10-18 17:33:28 +09:00
Syoyo Fujita
28ee1b42ce Merge pull request #103 from chrisliebert/master
Issue #74: Added basic diffuse texture support to examples/viewer
2016-10-08 14:02:11 +09:00
Chris Liebert
35889026d6 added additional bounds checking 2016-10-07 21:21:33 -07:00
Chris Liebert
e81ac971b0 Issue #74: Added basic diffuse texture support to examples/viewer using stb_image. The previously used color based on the normal is combined with the diffuse color from the material definition. 2016-10-07 20:58:04 -07:00
Syoyo Fujita
e05ce6aff0 Merge pull request #102 from Entalpi/master
Minor spelling fix
2016-10-07 11:40:33 +09:00
Alexander Lingtorp
e4598ba84a Minor spelling fix 2016-10-06 19:30:21 +02:00
Syoyo Fujita
79513077f3 Fix compilation on pre-C++11 compiler. 2016-10-02 17:37:02 +09:00
Syoyo Fujita
72acadca79 Merge pull request #101 from kavika13/master
Make it easier to use an existing stream reader
2016-10-02 17:35:36 +09:00
Merlyn Morgan-Graham
398e358826 Add unit tests for reading from existing file stream 2016-10-02 01:25:07 -07:00
Merlyn Morgan-Graham
7fc9b0fe97 Add stream based material reader implementation 2016-10-02 00:52:45 -07:00
Merlyn Morgan-Graham
71cc967f42 Allow skipping material reads on LoadObj istream overload 2016-10-02 00:52:19 -07:00
Syoyo Fujita
3ddad1e377 Merge pull request #97 from middlefeng/url-update
Metal viewer URL update.
2016-09-09 00:45:08 +09:00
middleware
92805d3088 Metal viewer URL update. 2016-09-07 06:31:08 -07:00
Syoyo Fujita
60acd05a03 Add brief README. 2016-09-06 02:18:24 +09:00
Syoyo Fujita
4a18e241d9 Measure zstd decompress time. 2016-09-06 02:11:23 +09:00
Syoyo Fujita
1b88a1e3c7 Support reading .obj from ZStd compressed format. 2016-09-04 19:34:05 +09:00
Syoyo Fujita
b3feefafdf Add link to MetalExamples. 2016-09-03 21:43:00 +09:00
Syoyo Fujita
a7a16db908 Add a link to GeeXLab. 2016-08-23 02:48:06 +09:00
Syoyo Fujita
9f92ba34a6 Update Feature list. 2016-08-23 02:44:32 +09:00
Syoyo Fujita
25cf039953 Add more link. 2016-08-23 02:37:47 +09:00
Syoyo Fujita
6bf145d7cf Add link to "Fast OBJ file importing and parsing in CUDA". 2016-08-23 02:35:31 +09:00
Syoyo Fujita
69240e18b4 Add link to PBGI. 2016-08-23 02:29:41 +09:00
Syoyo Fujita
5b9f431512 Update TODO list. 2016-08-20 18:16:50 +09:00
Syoyo Fujita
ad79762212 Update README. 2016-08-20 18:03:18 +09:00
Syoyo Fujita
64149943cc Add link to examples. 2016-08-20 18:01:27 +09:00
Syoyo Fujita
714194d353 Merge branch 'master' of github.com:syoyo/tinyobjloader 2016-08-20 17:58:57 +09:00
Syoyo Fujita
9d81c24934 Update AppVeyor script. 2016-08-20 17:54:24 +09:00
Syoyo Fujita
25c194bf71 Update README. 2016-08-20 17:53:07 +09:00
Syoyo Fujita
5a4c5ff668 Merge branch 'develop' 2016-08-20 17:49:59 +09:00
Syoyo Fujita
f06e814d7c Update README. 2016-08-20 16:23:41 +09:00
Syoyo Fujita
5eef2b0914 Fix python binding. 2016-08-20 16:23:20 +09:00
Syoyo Fujita
49988672f4 Add link to tinyobjloader-c. 2016-08-19 20:22:26 +09:00
Syoyo Fujita
d192402800 Support Tf in MTL. 2016-08-19 20:19:28 +09:00
Syoyo Fujita
646f1312f1 Parse 'Tf' field in MTL. 2016-08-18 16:13:27 +09:00
Syoyo Fujita
db62a6c1cc Merge pull request #95 from graysonlang/patch-1
Fix MTL token for transmission filter.
2016-08-18 11:49:51 +09:00
Grayson Lang
8e53519a27 Ooops, meant "Tf" not "TF". 2016-08-17 13:34:18 -07:00
Grayson Lang
d3d6932efd Fix MTL "transmission filter" token
The "transmission filter" is currently set to Kt, which is undocumented. Adding support for the specified token of "Tf".
2016-08-17 13:30:25 -07:00
Syoyo Fujita
dea325cdcb Fix face num calculation. 2016-08-13 19:27:13 +09:00
Syoyo Fujita
2b50b31657 Return parsed material information in parseObj API..
Parse PBR extension in MTL
2016-08-13 18:38:45 +09:00
Syoyo Fujita
110b49a0c8 Rename vertex_index struct. 2016-08-13 15:27:35 +09:00
Syoyo Fujita
319746d3db Fix parsing benchmark flag. 2016-08-13 15:09:58 +09:00
Syoyo Fujita
2cb73fa85d Fix the ordefing of a constructor call of vertex_index. 2016-08-13 02:52:36 +09:00
Syoyo Fujita
5a832b470a Update README. 2016-08-12 23:13:31 +09:00
Syoyo Fujita
42f04024d4 Remove old version log. 2016-08-12 23:13:05 +09:00
Syoyo Fujita
07852e206d Apply clang-format. 2016-08-12 23:12:46 +09:00
Syoyo Fujita
156b47760d Merge branch 'develop' of github.com:syoyo/tinyobjloader into develop 2016-08-12 20:40:16 +09:00
Syoyo Fujita
673501749f Apply clang-format. 2016-08-12 20:39:42 +09:00
Syoyo Fujita
b56fc1a0cc Fix appveyor script. 2016-08-12 19:01:51 +09:00
Syoyo Fujita
2ed3222bbe Compute geometric normal where vertex normal is not available. 2016-08-12 18:58:34 +09:00
Syoyo Fujita
c7da23795d Add static keyword to safeGetline(). 2016-08-05 19:16:46 +09:00
Syoyo Fujita
8ca2bc0de7 Fix appveyor script. 2016-08-02 17:14:46 +09:00
Syoyo Fujita
0a85945767 Skip trailing whitespace in mtl. Fixes #92. 2016-08-02 17:04:00 +09:00
Syoyo Fujita
51d13700d8 Skip trailing whitespace in mtl. Fixes #92. 2016-08-02 16:55:50 +09:00
Syoyo Fujita
1983e889dc Update callback API example.
Update API document.
2016-07-28 16:21:48 +09:00
Syoyo Fujita
dfe9d7bcae Merge pull request #91 from adishavit/develop
Changes the value indicating non-existent index to 0.
2016-07-28 16:16:38 +09:00
Adi Shavit @ MacBookPro
4dee4cc673 Changes the value indicating non-existent index to 0. OBJ indices are 1 (or -1) based, so 0 is an invalid value. 2016-07-28 10:12:16 +03:00
Syoyo Fujita
a7ea651bef Suppress clang warnings. 2016-07-28 00:30:52 +09:00
Syoyo Fujita
4c2afb8814 Merge pull request #90 from adishavit/develop
Changes the LoadObjWithCallback() API
2016-07-27 17:45:49 +09:00
Adi Shavit @ MacBookPro
951833812a Changes the LoadObjWithCallback() API to accept std::istream& instead of pointed since this is a required argument.
- Also changing the argument order to allow for defaults for optional arguments.
2016-07-27 11:38:25 +03:00
Syoyo Fujita
e456cc949d Merge pull request #89 from adishavit/develop
Crash bug fix + optimizations in LoadObjWithCallback()
2016-07-27 16:30:36 +09:00
Syoyo Fujita
75e64cd47a Support parsing w element of vertex coordinate, and also third element of texture coordinates. Fixes #88. 2016-07-27 16:16:51 +09:00
Adi Shavit @ MacBookPro
3736a5791f Fix a crash bug in LoadObjWithCallback() when passing a nullptr as MaterialReader *readMatFn.
- This preserves the existing API. If `nullptr` is passed then the material file will be ignored. This is useful when the user is not interested in the materials.
- Note that if `nullptr` is not a valid option for this function, then the API needs to be changed to take `MaterialReader&`.
2016-07-27 10:04:22 +03:00
Adi Shavit @ MacBookPro
0b0bf60137 Move heap-based objects like vectors and strings outside the parse loop in LoadObjWithCallback().
This avoids reallocation on every use making the code faster.
2016-07-27 10:00:13 +03:00
Syoyo Fujita
d496d8eab6 Remove some invalid comments. Fix calling index_cb per f line. Fixes #87. 2016-07-27 00:02:53 +09:00
Syoyo Fujita
22883def8d Support PBR extension for MTL which is proposed in http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr 2016-07-25 22:46:30 +09:00
Syoyo Fujita
5826f1e149 Merge branch 'develop' of github.com:syoyo/tinyobjloader into develop 2016-07-25 19:03:42 +09:00
Syoyo Fujita
ef6560298e Add Vulkan Tutorial to the list. 2016-07-25 19:02:51 +09:00
Syoyo Fujita
e3a56816d6 Use std::getline() to read arbitrary size of line data. 2016-07-24 15:53:05 +09:00
Syoyo Fujita
5ef400882a Fix pow computation. 2016-07-15 13:25:44 +09:00
Syoyo Fujita
333bb55d84 Revert memcpy'ing input string into local buffer. 2016-07-15 02:01:50 +09:00
Syoyo Fujita
7d6318c3ad Remove redundant & buggy memcpy.
Add verbose option.
2016-07-15 01:38:39 +09:00
Syoyo Fujita
d5c722125a Show parsing time. 2016-07-09 00:52:27 +09:00
Syoyo Fujita
16ed0ac129 Update README. 2016-07-06 01:36:16 +09:00
Syoyo Fujita
b69d2a2c55 Add more links. 2016-05-29 16:37:30 +09:00
Syoyo Fujita
e210379335 Add Probulator 2016-05-29 14:08:27 +09:00
Syoyo Fujita
659976b8f3 Merge branch 'master' of github.com:syoyo/tinyobjloader 2016-05-29 13:32:24 +09:00
Syoyo Fujita
96ba498d70 Add voxelizer. 2016-05-29 13:09:00 +09:00
Syoyo Fujita
7ecb0b2f37 Use sefe getline for files with different line breaks. Fixes #81. 2016-05-15 16:20:35 +09:00
Syoyo Fujita
a14bbdb065 Merge pull request #80 from Vazquinhos/master
Fixing errors normal generation no triangulation. Remove warnings. Coding Style.
2016-05-13 21:18:48 +09:00
Vazquinhos
bfedfbb1fb Merge remote-tracking branch 'refs/remotes/syoyo/master'
# Conflicts:
#	tiny_obj_loader.h
2016-05-13 14:03:03 +02:00
Vazquinhos
41f46c7fd7 Error fixed with no triangulation. Removed warnings and modified coding brace style 2016-05-13 13:54:27 +02:00
Vazquinhos
a62dd278e2 Merge remote-tracking branch 'refs/remotes/syoyo/master' 2016-05-13 13:19:09 +02:00
Syoyo Fujita
a20e4ede85 Update document and version. 2016-05-13 20:09:49 +09:00
Syoyo Fujita
1ab0d147cb Merge pull request #79 from Vazquinhos/master
Flat normals calculation of obj without normals
2016-05-13 19:46:47 +09:00
Vazquinhos
9aee576b99 Added dependencies to cmath in order to use sqrt 2016-05-13 12:37:24 +02:00
Vazquinhos
0dcc72239d Files deleted of compilation log 2016-05-13 12:29:42 +02:00
Vazquinhos
33d5e9aa07 Test.cc modified with the new flags 2016-05-13 12:28:10 +02:00
Vazquinhos
e528741a8b Flat normals calculation of objects that their normals are empty 2016-05-13 12:25:48 +02:00
27 changed files with 8929 additions and 860 deletions

View File

@@ -1,5 +1,4 @@
tinyobjloader # tinyobjloader
=============
[![Join the chat at https://gitter.im/syoyo/tinyobjloader](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/syoyo/tinyobjloader](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -17,29 +16,24 @@ Tiny but powerful single file wavefront obj loader written in C++. No dependency
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) `tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c .
What's new Notice!
----------
* XX YY, ZZZZ : New data strcutre and API!
* Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert!
* Nov 26, 2015 : Now single-header only!.
* Nov 08, 2015 : Improved API.
* Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors!
* Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel!
* Feb 06, 2015 : Fix parsing multi-material object
* Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo!
* Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran!
* Apr 29, 2014 : Add API to read .obj from std::istream. Good for reading compressed .obj or connecting to procedural primitive generator. Thanks burnse!
* Apr 21, 2014 : Define default material if no material definition exists in .obj. Thanks YarmUI!
* Apr 10, 2014 : Add support for parsing 'illum' and 'd'/'Tr' statements. Thanks mmp!
* Jan 27, 2014 : Added CMake project. Thanks bradc6!
* Nov 26, 2013 : Performance optimization by NeuralSandwich. 9% improvement in his project, thanks!
* Sep 12, 2013 : Added multiple .obj sticher example.
Example
------- -------
We have released new version v1.0.0 on 20 Aug, 2016.
Old version is available `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x
## What's new
* 20 Aug, 2016 : Bump version v1.0.0. New data strcutre and API!
### Old version
Previous old version is avaiable in `v0.9.x` branch.
## Example
![Rungholt](images/rungholt.jpg) ![Rungholt](images/rungholt.jpg)
tinyobjloader can successfully load 6M triangles Rungholt scene. tinyobjloader can successfully load 6M triangles Rungholt scene.
@@ -48,12 +42,21 @@ http://graphics.cs.williams.edu/data/meshes.xml
![](images/sanmugel.png) ![](images/sanmugel.png)
* [examples/viewer/](examples/viewer) OpenGL .obj viewer * [examples/viewer/](examples/viewer) OpenGL .obj viewer
* [examples/callback_api/](examples/callback_api/) Callback API example
* [examples/voxelize/](examples/voxelize/) Voxelizer example
Use case ## Use case
--------
TinyObjLoader is successfully used in ... TinyObjLoader is successfully used in ...
### New version(v1.0.x)
* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models
* .obj viewer with Metal https://github.com/middlefeng/NuoModelViewer/tree/master
* Your project here!
### Old version(v0.9.x)
* bullet3 https://github.com/erwincoumans/bullet3 * bullet3 https://github.com/erwincoumans/bullet3
* pbrt-v2 https://github.com/mmp/pbrt-v2 * pbrt-v2 https://github.com/mmp/pbrt-v2
* OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01
@@ -65,10 +68,19 @@ TinyObjLoader is successfully used in ...
* pbrt-v3 https://github.com/mmp/pbrt-v3 * pbrt-v3 https://github.com/mmp/pbrt-v3
* cocos2d-x https://github.com/cocos2d/cocos2d-x/ * cocos2d-x https://github.com/cocos2d/cocos2d-x/
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan * Android Vulkan demo https://github.com/SaschaWillems/Vulkan
* Your project here! * voxelizer https://github.com/karimnaaji/voxelizer
* Probulator https://github.com/kayru/Probulator
* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking
* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK
* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg
* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl
* Point Based Global Illumination on modern GPU https://pbgi.wordpress.com/code-source/
* Fast OBJ file importing and parsing in CUDA http://researchonline.jcu.edu.au/42515/1/2015.CVM.OBJCUDA.pdf
* Sorted Shading for Uni-Directional Pathtracing by Joshua Bainbridge https://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc15/02Josh/joshua_bainbridge_thesis.pdf
* GeeXLab http://www.geeks3d.com/hacklab/20160531/geexlab-0-12-0-0-released-for-windows/
Features
-------- ## Features
* Group(parse multiple group name) * Group(parse multiple group name)
* Vertex * Vertex
@@ -77,23 +89,23 @@ Features
* Material * Material
* Unknown material attributes are returned as key-value(value is string) map. * Unknown material attributes are returned as key-value(value is string) map.
* Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) * Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification)
* PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
* Callback API for custom loading. * Callback API for custom loading.
TODO ## TODO
----
* [ ] Fix Python binding.
* [ ] Fix obj_sticker example. * [ ] Fix obj_sticker example.
* [ ] More unit test codes. * [ ] More unit test codes.
* [ ] Texture options
* [ ] Normal vector generation
* [ ] Support smoothing groups
License ## License
-------
Licensed under MIT license. Licensed under MIT license.
Usage ## Usage
-----
`attrib_t` contains single and linear array of vertex data(position, normal and texcoord). `attrib_t` contains single and linear array of vertex data(position, normal and texcoord).
Each `shape_t` does not contain vertex data but contains array index to `attrib_t`. Each `shape_t` does not contain vertex data but contains array index to `attrib_t`.
@@ -148,8 +160,7 @@ for (size_t s = 0; s < shapes.size(); s++) {
``` ```
Optimized loader ## Optimized loader
----------------
Optimized multi-threaded .obj loader is available at `experimental/` directory. Optimized multi-threaded .obj loader is available at `experimental/` directory.
If you want absolute performance to load .obj data, this optimized loader will fit your purpose. If you want absolute performance to load .obj data, this optimized loader will fit your purpose.
@@ -163,7 +174,6 @@ Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core
* optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline) * optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline)
Tests ## Tests
-----
Unit tests are provided in `tests` directory. See `tests/README.md` for details. Unit tests are provided in `tests` directory. See `tests/README.md` for details.

View File

@@ -1,4 +1,4 @@
version: 0.9.{build} version: 1.0.{build}
platform: x64 platform: x64
@@ -7,7 +7,6 @@ install:
# All external dependencies are installed in C:\projects\deps # All external dependencies are installed in C:\projects\deps
####################################################################################### #######################################################################################
- mkdir C:\projects\deps - mkdir C:\projects\deps
- cd C:\projects\deps
####################################################################################### #######################################################################################
# Install Ninja # Install Ninja

View File

@@ -1,39 +1,44 @@
//
// An example of how to use callback API.
// This example is minimum and incomplete. Just showing the usage of callback
// API.
// You need to implement your own Mesh data struct constrution based on this
// example in practical.
//
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h" #include "tiny_obj_loader.h"
#include <cassert>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cassert> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <fstream>
typedef struct typedef struct {
{
std::vector<float> vertices; std::vector<float> vertices;
std::vector<float> normals; std::vector<float> normals;
std::vector<float> texcoords; std::vector<float> texcoords;
std::vector<int> v_indices; std::vector<int> v_indices;
std::vector<int> vn_indices; std::vector<int> vn_indices;
std::vector<int> vt_indices; std::vector<int> vt_indices;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
} MyMesh; } MyMesh;
void vertex_cb(void *user_data, float x, float y, float z) void vertex_cb(void *user_data, float x, float y, float z, float w) {
{ MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data); printf("v[%ld] = %f, %f, %f (w %f)\n", mesh->vertices.size() / 3, x, y, z, w);
printf("v[%ld] = %f, %f, %f\n", mesh->vertices.size() / 3, x, y, z);
mesh->vertices.push_back(x); mesh->vertices.push_back(x);
mesh->vertices.push_back(y); mesh->vertices.push_back(y);
mesh->vertices.push_back(z); mesh->vertices.push_back(z);
// Discard w
} }
void normal_cb(void *user_data, float x, float y, float z) void normal_cb(void *user_data, float x, float y, float z) {
{ MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z); printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z);
mesh->normals.push_back(x); mesh->normals.push_back(x);
@@ -41,49 +46,56 @@ void normal_cb(void *user_data, float x, float y, float z)
mesh->normals.push_back(z); mesh->normals.push_back(z);
} }
void texcoord_cb(void *user_data, float x, float y) void texcoord_cb(void *user_data, float x, float y, float z) {
{ MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data); printf("vt[%ld] = %f, %f, %f\n", mesh->texcoords.size() / 3, x, y, z);
printf("vt[%ld] = %f, %f\n", mesh->texcoords.size() / 2, x, y);
mesh->texcoords.push_back(x); mesh->texcoords.push_back(x);
mesh->texcoords.push_back(y); mesh->texcoords.push_back(y);
mesh->texcoords.push_back(z);
} }
void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx) void index_cb(void *user_data, tinyobj::index_t *indices, int num_indices) {
{
// NOTE: the value of each index is raw value. // NOTE: the value of each index is raw value.
// For example, the application must manually adjust the index with offset // For example, the application must manually adjust the index with offset
// (e.g. v_indices.size()) when the value is negative(relative index). // (e.g. v_indices.size()) when the value is negative(whic means relative
// index).
// Also, the first index starts with 1, not 0.
// See fixIndex() function in tiny_obj_loader.h for details. // See fixIndex() function in tiny_obj_loader.h for details.
// Also, -2147483648(0x80000000) is set for the index value which does not exist in .obj // Also, 0 is set for the index value which
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data); // does not exist in .obj
printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), v_idx, vn_idx, vt_idx); MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
if (v_idx != 0x80000000) { for (int i = 0; i < num_indices; i++) {
mesh->v_indices.push_back(v_idx); tinyobj::index_t idx = indices[i];
} printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), idx.vertex_index,
if (vn_idx != 0x80000000) { idx.normal_index, idx.texcoord_index);
mesh->vn_indices.push_back(vn_idx);
} if (idx.vertex_index != 0) {
if (vt_idx != 0x80000000) { mesh->v_indices.push_back(idx.vertex_index);
mesh->vt_indices.push_back(vt_idx); }
if (idx.normal_index != 0) {
mesh->vn_indices.push_back(idx.normal_index);
}
if (idx.texcoord_index != 0) {
mesh->vt_indices.push_back(idx.texcoord_index);
}
} }
} }
void usemtl_cb(void *user_data, const char* name, int material_idx) void usemtl_cb(void *user_data, const char *name, int material_idx) {
{ MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
if ((material_idx > -1) && (material_idx < mesh->materials.size())) { if ((material_idx > -1) && (material_idx < mesh->materials.size())) {
printf("usemtl. material id = %d(name = %s)\n", material_idx, mesh->materials[material_idx].name.c_str()); printf("usemtl. material id = %d(name = %s)\n", material_idx,
mesh->materials[material_idx].name.c_str());
} else { } else {
printf("usemtl. name = %s\n", name); printf("usemtl. name = %s\n", name);
} }
} }
void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_materials) void mtllib_cb(void *user_data, const tinyobj::material_t *materials,
{ int num_materials) {
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data); MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
printf("mtllib. # of materials = %d\n", num_materials); printf("mtllib. # of materials = %d\n", num_materials);
for (int i = 0; i < num_materials; i++) { for (int i = 0; i < num_materials; i++) {
@@ -91,9 +103,8 @@ void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_ma
} }
} }
void group_cb(void *user_data, const char **names, int num_names) void group_cb(void *user_data, const char **names, int num_names) {
{ // MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("group : name = \n"); printf("group : name = \n");
for (int i = 0; i < num_names; i++) { for (int i = 0; i < num_names; i++) {
@@ -101,16 +112,12 @@ void group_cb(void *user_data, const char **names, int num_names)
} }
} }
void object_cb(void *user_data, const char *name) void object_cb(void *user_data, const char *name) {
{ // MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("object : name = %s\n", name); printf("object : name = %s\n", name);
} }
int int main(int argc, char **argv) {
main(int argc, char** argv)
{
tinyobj::callback_t cb; tinyobj::callback_t cb;
cb.vertex_cb = vertex_cb; cb.vertex_cb = vertex_cb;
cb.normal_cb = normal_cb; cb.normal_cb = normal_cb;
@@ -123,7 +130,11 @@ main(int argc, char** argv)
MyMesh mesh; MyMesh mesh;
std::string err; std::string err;
std::ifstream ifs("../../models/cornell_box.obj"); std::string filename = "../../models/cornell_box.obj";
if (argc > 1) {
filename = std::string(argv[1]);
}
std::ifstream ifs(filename.c_str());
if (ifs.fail()) { if (ifs.fail()) {
std::cerr << "file not found." << std::endl; std::cerr << "file not found." << std::endl;
@@ -132,7 +143,7 @@ main(int argc, char** argv)
tinyobj::MaterialFileReader mtlReader("../../models/"); tinyobj::MaterialFileReader mtlReader("../../models/");
bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader); bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;

6755
examples/viewer/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,16 @@
// //
// Simple .obj viewer(vertex only) // Simple .obj viewer(vertex only)
// //
#include <vector> #include <algorithm>
#include <string> #include <cassert>
#include <cmath>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <cmath> #include <map>
#include <cassert> #include <string>
#include <algorithm> #include <vector>
#include <GL/glew.h> #include <GL/glew.h>
@@ -26,11 +27,23 @@
#include "trackball.h" #include "trackball.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#ifdef _WIN32 #ifdef _WIN32
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <windows.h> #include <windows.h>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#include <mmsystem.h> #include <mmsystem.h>
#ifdef __cplusplus #ifdef __cplusplus
} }
@@ -45,7 +58,7 @@ extern "C" {
#endif #endif
class timerutil { class timerutil {
public: public:
#ifdef _WIN32 #ifdef _WIN32
typedef DWORD time_t; typedef DWORD time_t;
@@ -81,7 +94,7 @@ public:
return (time_t)(t.tv_sec * 1000 + t.tv_usec); return (time_t)(t.tv_sec * 1000 + t.tv_usec);
} }
#else // C timer #else // C timer
// using namespace std; // using namespace std;
typedef clock_t time_t; typedef clock_t time_t;
@@ -96,7 +109,7 @@ public:
#endif #endif
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
DWORD t_[2]; DWORD t_[2];
#else #else
@@ -110,8 +123,9 @@ private:
}; };
typedef struct { typedef struct {
GLuint vb; // vertex buffer GLuint vb; // vertex buffer
int numTriangles; int numTriangles;
size_t material_id;
} DrawObject; } DrawObject;
std::vector<DrawObject> gDrawObjects; std::vector<DrawObject> gDrawObjects;
@@ -161,18 +175,21 @@ void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
} }
} }
bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& drawObjects, const char* filename) 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) {
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
timerutil tm; timerutil tm;
tm.start(); tm.start();
std::string err; std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL); bool ret =
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
} }
@@ -184,7 +201,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
return false; return false;
} }
printf("Parsing time: %d [ms]\n", tm.msec()); printf("Parsing time: %d [ms]\n", (int)tm.msec());
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3); printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3); printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
@@ -192,95 +209,183 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
printf("# of materials = %d\n", (int)materials.size()); printf("# of materials = %d\n", (int)materials.size());
printf("# of shapes = %d\n", (int)shapes.size()); printf("# of shapes = %d\n", (int)shapes.size());
// Append `default` material
materials.push_back(tinyobj::material_t());
// Load diffuse textures
{
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;
unsigned char* image = stbi_load(mp->diffuse_texname.c_str(), &w, &h, &comp, STBI_default);
if (image == nullptr) {
std::cerr << "Unable to load texture: " << mp->diffuse_texname << std::endl;
exit(1);
}
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_NEAREST);
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);
}
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(); bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max(); bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
{ {
for (size_t s = 0; s < shapes.size(); s++) { for (size_t s = 0; s < shapes.size(); s++) {
DrawObject o; DrawObject o;
std::vector<float> vb; // pos(3float), normal(3float), color(3float) std::vector<float> vb; // pos(3float), normal(3float), color(3float)
for (size_t f = 0; f < shapes[s].mesh.indices.size()/3; f++) { 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];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2];
tinyobj::index_t idx0 = shapes[s].mesh.indices[3*f+0]; int current_material_id = shapes[s].mesh.material_ids[f];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3*f+1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3*f+2];
float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3*f0+k];
v[1][k] = attrib.vertices[3*f1+k];
v[2][k] = attrib.vertices[3*f2+k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
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];
}
} 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++) {
vb.push_back(v[k][0]);
vb.push_back(v[k][1]);
vb.push_back(v[k][2]);
vb.push_back(n[k][0]);
vb.push_back(n[k][1]);
vb.push_back(n[k][2]);
// Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb.push_back(c[0] * 0.5 + 0.5);
vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5);
}
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`.
}
//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];
}
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);
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
tc[0][1] = 1.0f - attrib.texcoords[2 * idx0.texcoord_index + 1];
tc[1][0] = attrib.texcoords[2 * idx1.texcoord_index];
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 {
std::cerr << "Texcoordinates are not defined" << std::endl;
exit(2);
} }
o.vb = 0; float v[3][3];
o.numTriangles = 0; for (int k = 0; k < 3; k++) {
if (vb.size() > 0) { int f0 = idx0.vertex_index;
glGenBuffers(1, &o.vb); int f1 = idx1.vertex_index;
glBindBuffer(GL_ARRAY_BUFFER, o.vb); int f2 = idx2.vertex_index;
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW); assert(f0 >= 0);
o.numTriangles = vb.size() / 9 / 3; assert(f1 >= 0);
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles); assert(f2 >= 0);
v[0][k] = attrib.vertices[3 * f0 + k];
v[1][k] = attrib.vertices[3 * f1 + k];
v[2][k] = attrib.vertices[3 * f2 + k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
} }
gDrawObjects.push_back(o); 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];
}
} 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++) {
vb.push_back(v[k][0]);
vb.push_back(v[k][1]);
vb.push_back(v[k][2]);
vb.push_back(n[k][0]);
vb.push_back(n[k][1]);
vb.push_back(n[k][2]);
// 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 len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb.push_back(c[0] * 0.5 + 0.5);
vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5);
vb.push_back(tc[k][0]);
vb.push_back(tc[k][1]);
}
}
o.vb = 0;
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) {
// Base case
o.material_id = shapes[s].mesh.material_ids[s];
} else {
o.material_id = materials.size() - 1; // = ID for default material.
}
if (vb.size() > 0) {
glGenBuffers(1, &o.vb);
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0),
GL_STATIC_DRAW);
o.numTriangles = vb.size() / (3 + 3 + 3 + 2) * 3;
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
}
drawObjects->push_back(o);
} }
} }
@@ -290,8 +395,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
return true; return true;
} }
void reshapeFunc(GLFWwindow* window, int w, int h) void reshapeFunc(GLFWwindow* window, int w, int h) {
{
int fb_w, fb_h; int fb_w, fb_h;
// Get actual framebuffer size. // Get actual framebuffer size.
glfwGetFramebufferSize(window, &fb_w, &fb_h); glfwGetFramebufferSize(window, &fb_w, &fb_h);
@@ -307,84 +411,96 @@ void reshapeFunc(GLFWwindow* window, int w, int h)
height = h; height = h;
} }
void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) { void keyboardFunc(GLFWwindow* window, int key, int scancode, int action,
if(action == GLFW_PRESS || action == GLFW_REPEAT){ int mods) {
// Move camera (void)window;
float mv_x = 0, mv_y = 0, mv_z = 0; (void)scancode;
if(key == GLFW_KEY_K) mv_x += 1; (void)mods;
else if(key == GLFW_KEY_J) mv_x += -1; if (action == GLFW_PRESS || action == GLFW_REPEAT) {
else if(key == GLFW_KEY_L) mv_y += 1; // Move camera
else if(key == GLFW_KEY_H) mv_y += -1; float mv_x = 0, mv_y = 0, mv_z = 0;
else if(key == GLFW_KEY_P) mv_z += 1; if (key == GLFW_KEY_K)
else if(key == GLFW_KEY_N) mv_z += -1; mv_x += 1;
//camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05); else if (key == GLFW_KEY_J)
// Close window mv_x += -1;
if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE); else if (key == GLFW_KEY_L)
mv_y += 1;
else if (key == GLFW_KEY_H)
mv_y += -1;
else if (key == GLFW_KEY_P)
mv_z += 1;
else if (key == GLFW_KEY_N)
mv_z += -1;
// camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
// Close window
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE)
glfwSetWindowShouldClose(window, GL_TRUE);
//init_frame = true; // init_frame = true;
} }
} }
void clickFunc(GLFWwindow* window, int button, int action, int mods){ void clickFunc(GLFWwindow* window, int button, int action, int mods) {
if(button == GLFW_MOUSE_BUTTON_LEFT){ (void)window;
if(action == GLFW_PRESS){ (void)mods;
mouseLeftPressed = true; if (button == GLFW_MOUSE_BUTTON_LEFT) {
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); if (action == GLFW_PRESS) {
} else if(action == GLFW_RELEASE){ mouseLeftPressed = true;
mouseLeftPressed = false; trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
} } else if (action == GLFW_RELEASE) {
mouseLeftPressed = false;
} }
if(button == GLFW_MOUSE_BUTTON_RIGHT){ }
if(action == GLFW_PRESS){ if (button == GLFW_MOUSE_BUTTON_RIGHT) {
mouseRightPressed = true; if (action == GLFW_PRESS) {
} else if(action == GLFW_RELEASE){ mouseRightPressed = true;
mouseRightPressed = false; } else if (action == GLFW_RELEASE) {
} mouseRightPressed = false;
} }
if(button == GLFW_MOUSE_BUTTON_MIDDLE){ }
if(action == GLFW_PRESS){ if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
mouseMiddlePressed = true; if (action == GLFW_PRESS) {
} else if(action == GLFW_RELEASE){ mouseMiddlePressed = true;
mouseMiddlePressed = false; } else if (action == GLFW_RELEASE) {
} mouseMiddlePressed = false;
} }
}
} }
void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){ void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
(void)window;
float rotScale = 1.0f; float rotScale = 1.0f;
float transScale = 2.0f; float transScale = 2.0f;
if(mouseLeftPressed){ if (mouseLeftPressed) {
trackball(prev_quat, trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width,
rotScale * (2.0f * prevMouseX - width) / (float)width, rotScale * (height - 2.0f * prevMouseY) / (float)height,
rotScale * (height - 2.0f * prevMouseY) / (float)height, rotScale * (2.0f * mouse_x - width) / (float)width,
rotScale * (2.0f * mouse_x - width) / (float)width, rotScale * (height - 2.0f * mouse_y) / (float)height);
rotScale * (height - 2.0f * mouse_y) / (float)height);
add_quats(prev_quat, curr_quat, curr_quat); add_quats(prev_quat, curr_quat, curr_quat);
} else if (mouseMiddlePressed) { } else if (mouseMiddlePressed) {
eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width; eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width; lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; eye[1] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height;
} else if (mouseRightPressed) { } else if (mouseRightPressed) {
eye[2] += transScale * (mouse_y - prevMouseY) / (float)height; eye[2] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height; lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height;
} }
// Update mouse point // Update mouse point
prevMouseX = mouse_x; prevMouseX = mouse_x;
prevMouseY = mouse_y; prevMouseY = mouse_y;
} }
void Draw(const std::vector<DrawObject>& drawObjects) 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_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL); glPolygonMode(GL_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0); glPolygonOffset(1.0, 1.0);
glColor3f(1.0f, 1.0f, 1.0f); GLsizei stride = (3 + 3 + 3 + 2) * sizeof(float);
for (size_t i = 0; i < drawObjects.size(); i++) { for (size_t i = 0; i < drawObjects.size(); i++) {
DrawObject o = drawObjects[i]; DrawObject o = drawObjects[i];
if (o.vb < 1) { if (o.vb < 1) {
@@ -395,12 +511,20 @@ void Draw(const std::vector<DrawObject>& drawObjects)
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 36, (const void*)0); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3));
glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6)); std::string diffuse_texname = materials[o.material_id].diffuse_texname;
if (diffuse_texname.length() > 0) {
glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]);
}
glVertexPointer(3, GL_FLOAT, stride, (const void*)0);
glNormalPointer(GL_FLOAT, stride, (const void*)(sizeof(float) * 3));
glColorPointer(3, GL_FLOAT, stride, (const void*)(sizeof(float) * 6));
glTexCoordPointer(2, GL_FLOAT, stride, (const void*)(sizeof(float) * 9));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays"); CheckErrors("drawarrays");
glBindTexture(GL_TEXTURE_2D, 0);
} }
// draw wireframe // draw wireframe
@@ -419,8 +543,11 @@ void Draw(const std::vector<DrawObject>& drawObjects)
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 36, (const void*)0); glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); glVertexPointer(3, GL_FLOAT, stride, (const void*)0);
glNormalPointer(GL_FLOAT, stride, (const void*)(sizeof(float) * 3));
glColorPointer(3, GL_FLOAT, stride, (const void*)(sizeof(float) * 6));
glTexCoordPointer(2, GL_FLOAT, stride, (const void*)(sizeof(float) * 9));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays"); CheckErrors("drawarrays");
@@ -443,9 +570,7 @@ static void Init() {
up[2] = 0.0f; up[2] = 0.0f;
} }
int main(int argc, char** argv) {
int main(int argc, char **argv)
{
if (argc < 2) { if (argc < 2) {
std::cout << "Needs input.obj\n" << std::endl; std::cout << "Needs input.obj\n" << std::endl;
return 0; return 0;
@@ -453,16 +578,13 @@ int main(int argc, char **argv)
Init(); Init();
if (!glfwInit()) {
if(!glfwInit()){
std::cerr << "Failed to initialize GLFW." << std::endl; std::cerr << "Failed to initialize GLFW." << std::endl;
return -1; return -1;
} }
window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL); window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
if(window == NULL){ if (window == NULL) {
std::cerr << "Failed to open GLFW window. " << std::endl; std::cerr << "Failed to open GLFW window. " << std::endl;
glfwTerminate(); glfwTerminate();
return 1; return 1;
@@ -486,7 +608,9 @@ int main(int argc, char **argv)
reshapeFunc(window, width, height); reshapeFunc(window, width, height);
float bmin[3], bmax[3]; float bmin[3], bmax[3];
if (false == LoadObjAndConvert(bmin, bmax, gDrawObjects, argv[1])) { std::vector<tinyobj::material_t> materials;
std::map<std::string, GLuint> textures;
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures, argv[1])) {
return -1; return -1;
} }
@@ -498,18 +622,20 @@ int main(int argc, char **argv)
maxExtent = 0.5f * (bmax[2] - bmin[2]); maxExtent = 0.5f * (bmax[2] - bmin[2]);
} }
while(glfwWindowShouldClose(window) == GL_FALSE) { while (glfwWindowShouldClose(window) == GL_FALSE) {
glfwPollEvents(); glfwPollEvents();
glClearColor(0.1f, 0.2f, 0.3f, 1.0f); glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
// camera & rotate // camera & rotate
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
GLfloat mat[4][4]; GLfloat mat[4][4];
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]); gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
up[1], up[2]);
build_rotmatrix(mat, curr_quat); build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]); glMultMatrixf(&mat[0][0]);
@@ -517,9 +643,10 @@ int main(int argc, char **argv)
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent); glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
// Centerize object. // Centerize object.
glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2])); glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
-0.5 * (bmax[2] + bmin[2]));
Draw(gDrawObjects); Draw(gDrawObjects, materials, textures);
glfwSwapBuffers(window); glfwSwapBuffers(window);
} }

74
examples/voxelize/main.cc Normal file
View File

@@ -0,0 +1,74 @@
#define VOXELIZER_IMPLEMENTATION
#include "voxelizer.h"
#define TINYOBJLOADER_IMPLEMENTATION
#include "../../tiny_obj_loader.h"
bool Voxelize(const char* filename, float voxelsizex, float voxelsizey, float voxelsizez, float precision)
{
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
if (!err.empty()) {
printf("err: %s\n", err.c_str());
}
if (!ret) {
printf("failed to load : %s\n", filename);
return false;
}
if (shapes.size() == 0) {
printf("err: # of shapes are zero.\n");
return false;
}
// Only use first shape.
{
vx_mesh_t* mesh;
vx_mesh_t* result;
mesh = vx_mesh_alloc(attrib.vertices.size(), shapes[0].mesh.indices.size());
for (size_t f = 0; f < shapes[0].mesh.indices.size(); f++) {
mesh->indices[f] = shapes[0].mesh.indices[f].vertex_index;
}
for (size_t v = 0; v < attrib.vertices.size() / 3; v++) {
mesh->vertices[v].x = attrib.vertices[3*v+0];
mesh->vertices[v].y = attrib.vertices[3*v+1];
mesh->vertices[v].z = attrib.vertices[3*v+2];
}
result = vx_voxelize(mesh, voxelsizex, voxelsizey, voxelsizez, precision);
printf("Number of vertices: %ld\n", result->nvertices);
printf("Number of indices: %ld\n", result->nindices);
}
return true;
}
int
main(
int argc,
char** argv)
{
if (argc < 4) {
printf("Usage: voxelize input.obj voxelsizex voxelsizey voxelsizez precision\n");
exit(-1);
}
const char* filename = argv[1];
float voxelsizex = atof(argv[2]);
float voxelsizey = atof(argv[3]);
float voxelsizez = atof(argv[4]);
float prec = atof(argv[5]);
bool ret = Voxelize(filename, voxelsizex, voxelsizey, voxelsizez, prec);
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}

5
experimental/README.md Normal file
View File

@@ -0,0 +1,5 @@
Experimental code for .obj loader.
* Multi-threaded optimized parser : tinyobj_loader_opt.h
* zstd compressed .obj support. `--with-zstd` premake option.
* gzip compressed .obj support. `--with-zlib` premake option.

View File

@@ -3,6 +3,11 @@ newoption {
description = "Build with zlib." description = "Build with zlib."
} }
newoption {
trigger = "with-zstd",
description = "Build with ZStandard compression."
}
solution "objview" solution "objview"
-- location ( "build" ) -- location ( "build" )
configurations { "Release", "Debug" } configurations { "Release", "Debug" }
@@ -23,6 +28,15 @@ solution "objview"
links { 'z' } links { 'z' }
end end
if _OPTIONS['with-zstd'] then
print("with-zstd")
defines { 'ENABLE_ZSTD' }
-- Set path to zstd installed dir.
includedirs { '$$HOME/local/include' }
libdirs { '$$HOME/local/lib' }
links { 'zstd' }
end
-- Uncomment if you want address sanitizer(gcc/clang only) -- Uncomment if you want address sanitizer(gcc/clang only)
--buildoptions { "-fsanitize=address" } --buildoptions { "-fsanitize=address" }
--linkoptions { "-fsanitize=address" } --linkoptions { "-fsanitize=address" }

View File

@@ -292,6 +292,22 @@ typedef struct {
std::string bump_texname; // map_bump, bump std::string bump_texname; // map_bump, bump
std::string displacement_texname; // disp std::string displacement_texname; // disp
std::string alpha_texname; // map_d std::string alpha_texname; // map_d
// PBR extension
// http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
float roughness; // [0, 1] default 0
float metallic; // [0, 1] default 0
float sheen; // [0, 1] default 0
float clearcoat_thickness; // [0, 1] default 0
float clearcoat_roughness; // [0, 1] default 0
float anisotropy; // aniso. [0, 1] default 0
float anisotropy_rotation; // anisor. [0, 1] default 0
std::string roughness_texname; // map_Pr
std::string metallic_texname; // map_Pm
std::string sheen_texname; // map_Ps
std::string emissive_texname; // map_Ke
std::string normal_texname; // norm. For normal mapping.
std::map<std::string, std::string> unknown_parameter; std::map<std::string, std::string> unknown_parameter;
} material_t; } material_t;
@@ -301,19 +317,20 @@ typedef struct {
unsigned int length; unsigned int length;
} shape_t; } shape_t;
struct vertex_index { struct index_t {
int v_idx, vt_idx, vn_idx; int vertex_index, texcoord_index, normal_index;
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {}
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} explicit index_t(int idx)
vertex_index(int vidx, int vtidx, int vnidx) : vertex_index(idx), texcoord_index(idx), normal_index(idx) {}
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} index_t(int vidx, int vtidx, int vnidx)
: vertex_index(vidx), texcoord_index(vtidx), normal_index(vnidx) {}
}; };
typedef struct { typedef struct {
std::vector<float, lt::allocator<float> > vertices; std::vector<float, lt::allocator<float> > vertices;
std::vector<float, lt::allocator<float> > normals; std::vector<float, lt::allocator<float> > normals;
std::vector<float, lt::allocator<float> > texcoords; std::vector<float, lt::allocator<float> > texcoords;
std::vector<vertex_index, lt::allocator<vertex_index> > faces; std::vector<index_t, lt::allocator<index_t> > indices;
std::vector<int, lt::allocator<int> > face_num_verts; std::vector<int, lt::allocator<int> > face_num_verts;
std::vector<int, lt::allocator<int> > material_ids; std::vector<int, lt::allocator<int> > material_ids;
} attrib_t; } attrib_t;
@@ -386,12 +403,11 @@ static inline int fixIndex(int idx, int n) {
} }
// Parse raw triples: i, i/j/k, i//k, i/j // Parse raw triples: i, i/j/k, i//k, i/j
static vertex_index parseRawTriple(const char **token) { static index_t parseRawTriple(const char **token) {
vertex_index vi( index_t vi(
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
vi.v_idx = my_atoi((*token)); vi.vertex_index = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -404,8 +420,7 @@ static vertex_index parseRawTriple(const char **token) {
// i//k // i//k
if ((*token)[0] == '/') { if ((*token)[0] == '/') {
(*token)++; (*token)++;
vi.vn_idx = my_atoi((*token)); vi.normal_index = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -414,8 +429,7 @@ static vertex_index parseRawTriple(const char **token) {
} }
// i/j/k or i/j // i/j/k or i/j
vi.vt_idx = my_atoi((*token)); vi.texcoord_index = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -426,8 +440,7 @@ static vertex_index parseRawTriple(const char **token) {
// i/j/k // i/j/k
(*token)++; // skip '/' (*token)++; // skip '/'
vi.vn_idx = my_atoi((*token)); vi.normal_index = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -436,17 +449,17 @@ static vertex_index parseRawTriple(const char **token) {
} }
static inline bool parseString(ShortString *s, const char **token) { static inline bool parseString(ShortString *s, const char **token) {
skip_space(token); //(*token) += strspn((*token), " \t"); skip_space(token);
size_t e = until_space((*token)); // strcspn((*token), " \t\r"); size_t e = until_space((*token));
(*s)->insert((*s)->end(), (*token), (*token) + e); (*s)->insert((*s)->end(), (*token), (*token) + e);
(*token) += e; (*token) += e;
return true; return true;
} }
static inline int parseInt(const char **token) { static inline int parseInt(const char **token) {
skip_space(token); //(*token) += strspn((*token), " \t"); skip_space(token);
int i = my_atoi((*token)); int i = my_atoi((*token));
(*token) += until_space((*token)); // strcspn((*token), " \t\r"); (*token) += until_space((*token));
return i; return i;
} }
@@ -585,9 +598,9 @@ assemble :
{ {
// = pow(5.0, exponent); // = pow(5.0, exponent);
double a = 5.0; double a = 1.0;
for (int i = 0; i < exponent; i++) { for (int i = 0; i < exponent; i++) {
a = a * a; a = a * 5.0;
} }
*result = *result =
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); //(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
@@ -601,13 +614,12 @@ fail:
} }
static inline float parseFloat(const char **token) { static inline float parseFloat(const char **token) {
skip_space(token); //(*token) += strspn((*token), " \t"); skip_space(token);
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER #ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
float f = static_cast<float>(atof(*token)); float f = static_cast<float>(atof(*token));
(*token) += strcspn((*token), " \t\r"); (*token) += strcspn((*token), " \t\r");
#else #else
const char *end = const char *end = (*token) + until_space((*token));
(*token) + until_space((*token)); // strcspn((*token), " \t\r");
double val = 0.0; double val = 0.0;
tryParseDouble((*token), end, &val); tryParseDouble((*token), end, &val);
float f = static_cast<float>(val); float f = static_cast<float>(val);
@@ -665,6 +677,11 @@ static void LoadMtl(std::map<std::string, int> *material_map,
std::string linebuf(&buf[0]); std::string linebuf(&buf[0]);
// Trim trailing whitespace.
if (linebuf.size() > 0) {
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
}
// Trim newline '\r\n' or '\n' // Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) { if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\n') if (linebuf[linebuf.size() - 1] == '\n')
@@ -747,7 +764,8 @@ static void LoadMtl(std::map<std::string, int> *material_map,
} }
// transmittance // transmittance
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(&r, &g, &b, &token); parseFloat3(&r, &g, &b, &token);
@@ -795,6 +813,7 @@ static void LoadMtl(std::map<std::string, int> *material_map,
material.dissolve = parseFloat(&token); material.dissolve = parseFloat(&token);
continue; continue;
} }
if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2; token += 2;
// Invert value of Tr(assume Tr is in range [0, 1]) // Invert value of Tr(assume Tr is in range [0, 1])
@@ -802,6 +821,55 @@ static void LoadMtl(std::map<std::string, int> *material_map,
continue; continue;
} }
// PBR: roughness
if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2;
material.roughness = parseFloat(&token);
continue;
}
// PBR: metallic
if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) {
token += 2;
material.metallic = parseFloat(&token);
continue;
}
// PBR: sheen
if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) {
token += 2;
material.sheen = parseFloat(&token);
continue;
}
// PBR: clearcoat thickness
if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) {
token += 2;
material.clearcoat_thickness = parseFloat(&token);
continue;
}
// PBR: clearcoat roughness
if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) {
token += 4;
material.clearcoat_roughness = parseFloat(&token);
continue;
}
// PBR: anisotropy
if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) {
token += 6;
material.anisotropy = parseFloat(&token);
continue;
}
// PBR: anisotropy rotation
if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) {
token += 7;
material.anisotropy_rotation = parseFloat(&token);
continue;
}
// ambient texture // ambient texture
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
@@ -858,6 +926,41 @@ static void LoadMtl(std::map<std::string, int> *material_map,
continue; continue;
} }
// PBR: roughness texture
if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
token += 7;
material.roughness_texname = token;
continue;
}
// PBR: metallic texture
if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
token += 7;
material.metallic_texname = token;
continue;
}
// PBR: sheen texture
if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
token += 7;
material.sheen_texname = token;
continue;
}
// PBR: emissive texture
if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
token += 7;
material.emissive_texname = token;
continue;
}
// PBR: normal map texture
if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
token += 5;
material.normal_texname = token;
continue;
}
// unknown parameter // unknown parameter
const char *_space = strchr(token, ' '); const char *_space = strchr(token, ' ');
if (!_space) { if (!_space) {
@@ -896,8 +999,8 @@ typedef struct {
float tx, ty; float tx, ty;
// for f // for f
std::vector<vertex_index, lt::allocator<vertex_index> > f; std::vector<index_t, lt::allocator<index_t> > f;
// std::vector<vertex_index> f; // std::vector<index_t> f;
std::vector<int, lt::allocator<int> > f_num_verts; std::vector<int, lt::allocator<int> > f_num_verts;
const char *group_name; const char *group_name;
@@ -918,44 +1021,42 @@ struct CommandCount {
size_t num_vn; size_t num_vn;
size_t num_vt; size_t num_vt;
size_t num_f; size_t num_f;
size_t num_faces; size_t num_indices;
CommandCount() { CommandCount() {
num_v = 0; num_v = 0;
num_vn = 0; num_vn = 0;
num_vt = 0; num_vt = 0;
num_f = 0; num_f = 0;
num_faces = 0; num_indices = 0;
} }
}; };
class class LoadOption {
LoadOption
{
public: public:
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {} LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
int req_num_threads;
bool triangulate;
bool verbose;
int req_num_threads;
bool triangulate;
bool verbose;
}; };
/// Parse wavefront .obj(.obj string data is expanded to linear char array /// Parse wavefront .obj(.obj string data is expanded to linear char array
/// `buf') /// `buf')
/// -1 to req_num_threads use the number of HW threads in the running system. /// -1 to req_num_threads use the number of HW threads in the running system.
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf, bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
size_t len, const LoadOption& option); std::vector<material_t> *materials, const char *buf, size_t len,
const LoadOption &option);
#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION #ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
static bool parseLine(Command *command, const char *p, size_t p_len, static bool parseLine(Command *command, const char *p, size_t p_len,
bool triangulate = true) { bool triangulate = true) {
// @todo { operate directly on pointer `p'. to do that, add range check for
// string operatoion against `p', since `p' is not null-terminated at p[p_len]
// }
char linebuf[4096]; char linebuf[4096];
assert(p_len < 4095); assert(p_len < 4095);
// StackVector<char, 256> linebuf; memcpy(linebuf, p, p_len);
// linebuf->resize(p_len + 1);
memcpy(&linebuf, p, p_len);
linebuf[p_len] = '\0'; linebuf[p_len] = '\0';
const char *token = linebuf; const char *token = linebuf;
@@ -963,8 +1064,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
command->type = COMMAND_EMPTY; command->type = COMMAND_EMPTY;
// Skip leading space. // Skip leading space.
// token += strspn(token, " \t"); skip_space(&token);
skip_space(&token); //(*token) += strspn((*token), " \t");
assert(token); assert(token);
if (token[0] == '\0') { // empty line if (token[0] == '\0') { // empty line
@@ -978,7 +1078,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// vertex // vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) { if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2; token += 2;
float x, y, z; float x = 0.0f, y = 0.0f, z = 0.0f;
parseFloat3(&x, &y, &z, &token); parseFloat3(&x, &y, &z, &token);
command->vx = x; command->vx = x;
command->vy = y; command->vy = y;
@@ -990,7 +1090,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// normal // normal
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
token += 3; token += 3;
float x, y, z; float x = 0.0f, y = 0.0f, z = 0.0f;
parseFloat3(&x, &y, &z, &token); parseFloat3(&x, &y, &z, &token);
command->nx = x; command->nx = x;
command->ny = y; command->ny = y;
@@ -1002,7 +1102,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// texcoord // texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3; token += 3;
float x, y; float x = 0.0f, y = 0.0f;
parseFloat2(&x, &y, &token); parseFloat2(&x, &y, &token);
command->tx = x; command->tx = x;
command->ty = y; command->ty = y;
@@ -1013,19 +1113,12 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// face // face
if (token[0] == 'f' && IS_SPACE((token[1]))) { if (token[0] == 'f' && IS_SPACE((token[1]))) {
token += 2; token += 2;
// token += strspn(token, " \t");
skip_space(&token); skip_space(&token);
StackVector<vertex_index, 8> f; StackVector<index_t, 8> f;
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
vertex_index vi = parseRawTriple(&token); index_t vi = parseRawTriple(&token);
// printf("v = %d, %d, %d\n", vi.v_idx, vi.vn_idx, vi.vt_idx);
// if (callback.index_cb) {
// callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx);
//}
// size_t n = strspn(token, " \t\r");
// token += n;
skip_space_and_cr(&token); skip_space_and_cr(&token);
f->push_back(vi); f->push_back(vi);
@@ -1034,9 +1127,9 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
command->type = COMMAND_F; command->type = COMMAND_F;
if (triangulate) { if (triangulate) {
vertex_index i0 = f[0]; index_t i0 = f[0];
vertex_index i1(-1); index_t i1(-1);
vertex_index i2 = f[1]; index_t i2 = f[1];
for (size_t k = 2; k < f->size(); k++) { for (size_t k = 2; k < f->size(); k++) {
i1 = i2; i1 = i2;
@@ -1153,27 +1246,28 @@ static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
return false; return false;
} }
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf, bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
size_t len, const LoadOption& option) std::vector<material_t> *materials, const char *buf, size_t len,
{ const LoadOption &option) {
attrib->vertices.clear(); attrib->vertices.clear();
attrib->normals.clear(); attrib->normals.clear();
attrib->texcoords.clear(); attrib->texcoords.clear();
attrib->faces.clear(); attrib->indices.clear();
attrib->face_num_verts.clear(); attrib->face_num_verts.clear();
attrib->material_ids.clear(); attrib->material_ids.clear();
shapes->clear(); shapes->clear();
if (len < 1) return false; if (len < 1) return false;
auto num_threads = (option.req_num_threads < 0) ? std::thread::hardware_concurrency() auto num_threads = (option.req_num_threads < 0)
: option.req_num_threads; ? std::thread::hardware_concurrency()
: option.req_num_threads;
num_threads = num_threads =
std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads)); std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
if (option.verbose) { if (option.verbose) {
std::cout << "# of threads = " << num_threads << std::endl; std::cout << "# of threads = " << num_threads << std::endl;
} }
auto t1 = std::chrono::high_resolution_clock::now(); auto t1 = std::chrono::high_resolution_clock::now();
@@ -1304,7 +1398,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
command_count[t].num_vt++; command_count[t].num_vt++;
} else if (command.type == COMMAND_F) { } else if (command.type == COMMAND_F) {
command_count[t].num_f += command.f.size(); command_count[t].num_f += command.f.size();
command_count[t].num_faces++; command_count[t].num_indices += command.f_num_verts.size();
} }
if (command.type == COMMAND_MTLLIB) { if (command.type == COMMAND_MTLLIB) {
@@ -1329,7 +1423,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
} }
std::map<std::string, int> material_map; std::map<std::string, int> material_map;
std::vector<material_t> materials;
// Load material(if exits) // Load material(if exits)
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 && if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
@@ -1344,7 +1437,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
std::ifstream ifs(material_filename); std::ifstream ifs(material_filename);
if (ifs.good()) { if (ifs.good()) {
LoadMtl(&material_map, &materials, &ifs); LoadMtl(&material_map, materials, &ifs);
// std::cout << "maetrials = " << materials.size() << std::endl; // std::cout << "maetrials = " << materials.size() << std::endl;
@@ -1368,14 +1461,15 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
size_t num_vn = 0; size_t num_vn = 0;
size_t num_vt = 0; size_t num_vt = 0;
size_t num_f = 0; size_t num_f = 0;
size_t num_faces = 0; size_t num_indices = 0;
for (size_t t = 0; t < num_threads; t++) { for (size_t t = 0; t < num_threads; t++) {
num_v += command_count[t].num_v; num_v += command_count[t].num_v;
num_vn += command_count[t].num_vn; num_vn += command_count[t].num_vn;
num_vt += command_count[t].num_vt; num_vt += command_count[t].num_vt;
num_f += command_count[t].num_f; num_f += command_count[t].num_f;
num_faces += command_count[t].num_faces; num_indices += command_count[t].num_indices;
} }
// std::cout << "# v " << num_v << std::endl; // std::cout << "# v " << num_v << std::endl;
// std::cout << "# vn " << num_vn << std::endl; // std::cout << "# vn " << num_vn << std::endl;
// std::cout << "# vt " << num_vt << std::endl; // std::cout << "# vt " << num_vt << std::endl;
@@ -1389,9 +1483,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
attrib->vertices.resize(num_v * 3); attrib->vertices.resize(num_v * 3);
attrib->normals.resize(num_vn * 3); attrib->normals.resize(num_vn * 3);
attrib->texcoords.resize(num_vt * 2); attrib->texcoords.resize(num_vt * 2);
attrib->faces.resize(num_f); attrib->indices.resize(num_f);
attrib->face_num_verts.resize(num_faces); attrib->face_num_verts.resize(num_indices);
attrib->material_ids.resize(num_faces); attrib->material_ids.resize(num_indices);
size_t v_offsets[kMaxThreads]; size_t v_offsets[kMaxThreads];
size_t n_offsets[kMaxThreads]; size_t n_offsets[kMaxThreads];
@@ -1410,7 +1504,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn; n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn;
t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt; t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt;
f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f; f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f;
face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_faces; face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_indices;
} }
StackVector<std::thread, 16> workers; StackVector<std::thread, 16> workers;
@@ -1456,17 +1550,20 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
t_count++; t_count++;
} else if (commands[t][i].type == COMMAND_F) { } else if (commands[t][i].type == COMMAND_F) {
for (size_t k = 0; k < commands[t][i].f.size(); k++) { for (size_t k = 0; k < commands[t][i].f.size(); k++) {
vertex_index &vi = commands[t][i].f[k]; index_t &vi = commands[t][i].f[k];
int v_idx = fixIndex(vi.v_idx, v_count); int vertex_index = fixIndex(vi.vertex_index, v_count);
int vn_idx = fixIndex(vi.vn_idx, n_count); int texcoord_index = fixIndex(vi.texcoord_index, t_count);
int vt_idx = fixIndex(vi.vt_idx, t_count); int normal_index = fixIndex(vi.normal_index, n_count);
attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx); attrib->indices[f_count + k] =
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];
} }
attrib->material_ids[face_count] = material_id;
attrib->face_num_verts[face_count] = commands[t][i].f.size();
f_count += commands[t][i].f.size(); f_count += commands[t][i].f.size();
face_count++; face_count += commands[t][i].f_num_verts.size();
} }
} }
})); }));
@@ -1556,21 +1653,21 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
} }
std::chrono::duration<double, std::milli> ms_total = t4 - t1; std::chrono::duration<double, std::milli> ms_total = t4 - t1;
if (option.verbose) { if (option.verbose) {
std::cout << "total parsing time: " << ms_total.count() << " ms\n"; std::cout << "total parsing time: " << ms_total.count() << " ms\n";
std::cout << " line detection : " << ms_linedetection.count() << " ms\n"; std::cout << " line detection : " << ms_linedetection.count() << " ms\n";
std::cout << " alloc buf : " << ms_alloc.count() << " ms\n"; std::cout << " alloc buf : " << ms_alloc.count() << " ms\n";
std::cout << " parse : " << ms_parse.count() << " ms\n"; std::cout << " parse : " << ms_parse.count() << " ms\n";
std::cout << " merge : " << ms_merge.count() << " ms\n"; std::cout << " merge : " << ms_merge.count() << " ms\n";
std::cout << " construct : " << ms_construct.count() << " ms\n"; std::cout << " construct : " << ms_construct.count() << " ms\n";
std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n"; std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n";
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl; std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
std::cout << "# of normals = " << attrib->normals.size() << std::endl; std::cout << "# of normals = " << attrib->normals.size() << std::endl;
std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl; std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl;
std::cout << "# of face indices = " << attrib->faces.size() << std::endl; std::cout << "# of face indices = " << attrib->indices.size() << std::endl;
std::cout << "# of faces = " << attrib->material_ids.size() << std::endl; std::cout << "# of indices = " << attrib->material_ids.size() << std::endl;
std::cout << "# of shapes = " << shapes->size() << std::endl; std::cout << "# of shapes = " << shapes->size() << std::endl;
} }
return true; return true;
} }

View File

@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
} }
static void vcopy(const float *v1, float *v2) { static void vcopy(const float *v1, float *v2) {
register int i; int i;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
v2[i] = v1[i]; v2[i] = v1[i];
} }

View File

@@ -16,6 +16,10 @@
#include <zlib.h> #include <zlib.h>
#endif #endif
#if defined(ENABLE_ZSTD)
#include <zstd.h>
#endif
#include <GL/glew.h> #include <GL/glew.h>
#ifdef __APPLE__ #ifdef __APPLE__
@@ -182,6 +186,71 @@ bool gz_load(std::vector<char>* buf, const char* filename)
#endif #endif
} }
#ifdef ENABLE_ZSTD
static off_t fsize_X(const char *filename)
{
struct stat st;
if (stat(filename, &st) == 0) return st.st_size;
/* error */
printf("stat: %s : %s \n", filename, strerror(errno));
exit(1);
}
static FILE* fopen_X(const char *filename, const char *instruction)
{
FILE* const inFile = fopen(filename, instruction);
if (inFile) return inFile;
/* error */
printf("fopen: %s : %s \n", filename, strerror(errno));
exit(2);
}
static void* malloc_X(size_t size)
{
void* const buff = malloc(size);
if (buff) return buff;
/* error */
printf("malloc: %s \n", strerror(errno));
exit(3);
}
#endif
bool zstd_load(std::vector<char>* buf, const char* filename)
{
#ifdef ENABLE_ZSTD
off_t const buffSize = fsize_X(filename);
FILE* const inFile = fopen_X(filename, "rb");
void* const buffer = malloc_X(buffSize);
size_t const readSize = fread(buffer, 1, buffSize, inFile);
if (readSize != (size_t)buffSize) {
printf("fread: %s : %s \n", filename, strerror(errno));
exit(4);
}
fclose(inFile);
unsigned long long const rSize = ZSTD_getDecompressedSize(buffer, buffSize);
if (rSize==0) {
printf("%s : original size unknown \n", filename);
exit(5);
}
buf->resize(rSize);
size_t const dSize = ZSTD_decompress(buf->data(), rSize, buffer, buffSize);
if (dSize != rSize) {
printf("error decoding %s : %s \n", filename, ZSTD_getErrorName(dSize));
exit(7);
}
free(buffer);
return true;
#else
return false;
#endif
}
const char* get_file_data(size_t *len, const char* filename) const char* get_file_data(size_t *len, const char* filename)
{ {
@@ -204,6 +273,20 @@ const char* get_file_data(size_t *len, const char* filename)
data_len = buf.size(); data_len = buf.size();
} }
} else if (strcmp(ext, ".zst") == 0) {
printf("zstd\n");
// Zstandard data.
std::vector<char> buf;
bool ret = zstd_load(&buf, filename);
if (ret) {
char *p = static_cast<char*>(malloc(buf.size() + 1)); // @fixme { implement deleter }
memcpy(p, &buf.at(0), buf.size());
p[buf.size()] = '\0';
data = p;
data_len = buf.size();
}
} else { } else {
data = mmap_file(&data_len, filename); data = mmap_file(&data_len, filename);
@@ -214,26 +297,39 @@ const char* get_file_data(size_t *len, const char* filename)
} }
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads) bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose)
{ {
#if 1
tinyobj_opt::attrib_t attrib; tinyobj_opt::attrib_t attrib;
std::vector<tinyobj_opt::shape_t> shapes; std::vector<tinyobj_opt::shape_t> shapes;
std::vector<tinyobj_opt::material_t> materials;
auto load_t_begin = std::chrono::high_resolution_clock::now();
size_t data_len = 0; size_t data_len = 0;
const char* data = get_file_data(&data_len, filename); const char* data = get_file_data(&data_len, filename);
if (data == nullptr) { if (data == nullptr) {
printf("failed to load file\n");
exit(-1); exit(-1);
return false; return false;
} }
printf("filesize: %d\n", (int)data_len); auto load_t_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> load_ms = load_t_end - load_t_begin;
if (verbose) {
std::cout << "filesize: " << data_len << std::endl;
std::cout << "load time: " << load_ms.count() << " [msecs]" << std::endl;
}
tinyobj_opt::LoadOption option; tinyobj_opt::LoadOption option;
option.req_num_threads = num_threads; option.req_num_threads = num_threads;
bool ret = parseObj(&attrib, &shapes, data, data_len, option); option.verbose = verbose;
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max(); bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max(); bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
//std::cout << "vertices.size() = " << attrib.vertices.size() << std::endl;
//std::cout << "normals.size() = " << attrib.normals.size() << std::endl;
{ {
DrawObject o; DrawObject o;
std::vector<float> vb; // pos(3float), normal(3float), color(3float) std::vector<float> vb; // pos(3float), normal(3float), color(3float)
@@ -241,15 +337,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) { 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.
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) { for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0]; tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0];
tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1]; tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1];
tinyobj_opt::vertex_index idx2 = attrib.faces[face_offset+3*f+2]; tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2];
float v[3][3]; float v[3][3];
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
int f0 = idx0.v_idx; int f0 = idx0.vertex_index;
int f1 = idx1.v_idx; int f1 = idx1.vertex_index;
int f2 = idx2.v_idx; int f2 = idx2.vertex_index;
assert(f0 >= 0); assert(f0 >= 0);
assert(f1 >= 0); assert(f1 >= 0);
assert(f2 >= 0); assert(f2 >= 0);
@@ -268,19 +364,24 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
float n[3][3]; float n[3][3];
if (attrib.normals.size() > 0) { if (attrib.normals.size() > 0) {
int f0 = idx0.vn_idx; int nf0 = idx0.normal_index;
int f1 = idx1.vn_idx; int nf1 = idx1.normal_index;
int f2 = idx2.vn_idx; int nf2 = idx2.normal_index;
assert(f0 >= 0);
assert(f1 >= 0); if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) {
assert(f2 >= 0); assert(3*nf0+2 < attrib.normals.size());
assert(3*f0+2 < attrib.normals.size()); assert(3*nf1+2 < attrib.normals.size());
assert(3*f1+2 < attrib.normals.size()); assert(3*nf2+2 < attrib.normals.size());
assert(3*f2+2 < attrib.normals.size()); for (int k = 0; k < 3; k++) {
for (int k = 0; k < 3; k++) { n[0][k] = attrib.normals[3*nf0+k];
n[0][k] = attrib.normals[3*f0+k]; n[1][k] = attrib.normals[3*nf1+k];
n[1][k] = attrib.normals[3*f1+k]; n[2][k] = attrib.normals[3*nf2+k];
n[2][k] = attrib.normals[3*f2+k]; }
} 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];
} }
} else { } else {
// compute geometric normal // compute geometric normal
@@ -299,7 +400,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
// Use normal as color. // Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]}; float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) { if (len2 > 1.0e-6f) {
float len = sqrtf(len2); float len = sqrtf(len2);
c[0] /= len; c[0] /= len;
@@ -330,9 +431,6 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
return true; return true;
#else
return false;
#endif
} }
void reshapeFunc(GLFWwindow* window, int w, int h) void reshapeFunc(GLFWwindow* window, int w, int h)
@@ -499,35 +597,45 @@ static void Init() {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc < 2) { if (argc < 2) {
std::cout << "Needs input.obj\n" << std::endl; std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << std::endl;
return 0; return 0;
} }
bool benchmark_only = false; bool benchmark_only = false;
int num_threads = -1; int num_threads = -1;
bool verbose = false;
if (argc > 2) { if (argc > 2) {
num_threads = atoi(argv[2]); num_threads = atoi(argv[2]);
} }
if (argc > 3) { if (argc > 3) {
benchmark_only = true; benchmark_only = (atoi(argv[3]) > 0) ? true : false;
}
if (argc > 4) {
verbose = true;
} }
if (benchmark_only) { if (benchmark_only) {
tinyobj_opt::attrib_t attrib; tinyobj_opt::attrib_t attrib;
std::vector<tinyobj_opt::shape_t> shapes; std::vector<tinyobj_opt::shape_t> shapes;
std::vector<tinyobj_opt::material_t> materials;
size_t data_len = 0; size_t data_len = 0;
const char* data = get_file_data(&data_len, argv[1]); const char* data = get_file_data(&data_len, argv[1]);
if (data == nullptr) { if (data == nullptr) {
printf("failed to load file\n");
exit(-1); exit(-1);
return false; return false;
} }
printf("filesize: %d\n", (int)data_len); printf("filesize: %d\n", (int)data_len);
tinyobj_opt::LoadOption option; tinyobj_opt::LoadOption option;
option.req_num_threads = num_threads; option.req_num_threads = num_threads;
bool ret = parseObj(&attrib, &shapes, data, data_len, option); option.verbose = true;
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
return ret; return ret;
} }
@@ -569,7 +677,7 @@ int main(int argc, char **argv)
reshapeFunc(window, width, height); reshapeFunc(window, width, height);
float bmin[3], bmax[3]; float bmin[3], bmax[3];
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads)) { if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) {
printf("failed to load & conv\n"); printf("failed to load & conv\n");
return -1; return -1;
} }

View File

@@ -4,19 +4,19 @@
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h" #include "tiny_obj_loader.h"
#include <cassert>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cassert> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <fstream>
#ifdef _WIN32 #ifdef _WIN32
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
#include <windows.h>
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@@ -30,7 +30,7 @@ extern "C" {
#endif #endif
class timerutil { class timerutil {
public: public:
#ifdef _WIN32 #ifdef _WIN32
typedef DWORD time_t; typedef DWORD time_t;
@@ -58,7 +58,8 @@ public:
static_cast<time_t>((tv[1].tv_usec - tv[0].tv_usec) / 1000); static_cast<time_t>((tv[1].tv_usec - tv[0].tv_usec) / 1000);
} }
time_t usec() { time_t usec() {
return this->sec() * 1000000 + static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec); return this->sec() * 1000000 +
static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
} }
time_t current() { time_t current() {
struct timeval t; struct timeval t;
@@ -66,7 +67,7 @@ public:
return static_cast<time_t>(t.tv_sec * 1000 + t.tv_usec); return static_cast<time_t>(t.tv_sec * 1000 + t.tv_usec);
} }
#else // C timer #else // C timer
// using namespace std; // using namespace std;
typedef clock_t time_t; typedef clock_t time_t;
@@ -81,7 +82,7 @@ public:
#endif #endif
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
DWORD t_[2]; DWORD t_[2];
#else #else
@@ -94,96 +95,103 @@ private:
#endif #endif
}; };
static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials) static void PrintInfo(const tinyobj::attrib_t& attrib,
{ const std::vector<tinyobj::shape_t>& shapes,
const std::vector<tinyobj::material_t>& materials) {
std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl;
std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl;
std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl; std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2)
<< std::endl;
std::cout << "# of shapes : " << shapes.size() << std::endl; std::cout << "# of shapes : " << shapes.size() << std::endl;
std::cout << "# of materials : " << materials.size() << std::endl; std::cout << "# of materials : " << materials.size() << std::endl;
for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { for (size_t v = 0; v < attrib.vertices.size() / 3; v++) {
printf(" v[%ld] = (%f, %f, %f)\n", static_cast<long>(v), printf(" v[%ld] = (%f, %f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.vertices[3*v+0]), static_cast<const double>(attrib.vertices[3 * v + 0]),
static_cast<const double>(attrib.vertices[3*v+1]), static_cast<const double>(attrib.vertices[3 * v + 1]),
static_cast<const double>(attrib.vertices[3*v+2])); static_cast<const double>(attrib.vertices[3 * v + 2]));
} }
for (size_t v = 0; v < attrib.normals.size() / 3; v++) { for (size_t v = 0; v < attrib.normals.size() / 3; v++) {
printf(" n[%ld] = (%f, %f, %f)\n", static_cast<long>(v), printf(" n[%ld] = (%f, %f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.normals[3*v+0]), static_cast<const double>(attrib.normals[3 * v + 0]),
static_cast<const double>(attrib.normals[3*v+1]), static_cast<const double>(attrib.normals[3 * v + 1]),
static_cast<const double>(attrib.normals[3*v+2])); static_cast<const double>(attrib.normals[3 * v + 2]));
} }
for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) {
printf(" uv[%ld] = (%f, %f)\n", static_cast<long>(v), printf(" uv[%ld] = (%f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.texcoords[2*v+0]), static_cast<const double>(attrib.texcoords[2 * v + 0]),
static_cast<const double>(attrib.texcoords[2*v+1])); static_cast<const double>(attrib.texcoords[2 * v + 1]));
} }
// For each shape // For each shape
for (size_t i = 0; i < shapes.size(); i++) { 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("shape[%ld].name = %s\n", static_cast<long>(i),
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.indices.size())); shapes[i].name.c_str());
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.indices.size()));
size_t index_offset = 0; 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.material_ids.size());
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size())); printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
// For each face // For each face
for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) { for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) {
size_t fnum = shapes[i].mesh.num_face_vertices[f]; size_t fnum = shapes[i].mesh.num_face_vertices[f];
printf(" face[%ld].fnum = %ld\n", static_cast<long>(f), static_cast<unsigned long>(fnum)); printf(" face[%ld].fnum = %ld\n", static_cast<long>(f),
static_cast<unsigned long>(fnum));
// For each vertex in the face // For each vertex in the face
for (size_t v = 0; v < fnum; v++) { for (size_t v = 0; v < fnum; v++) {
tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v]; tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v];
printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast<long>(f), static_cast<long>(v), idx.vertex_index, idx.normal_index, idx.texcoord_index); printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast<long>(f),
} static_cast<long>(v), idx.vertex_index, idx.normal_index,
idx.texcoord_index);
}
printf(" face[%ld].material_id = %d\n", static_cast<long>(f), shapes[i].mesh.material_ids[f]); printf(" face[%ld].material_id = %d\n", static_cast<long>(f),
shapes[i].mesh.material_ids[f]);
index_offset += fnum; index_offset += fnum;
} }
printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.tags.size())); printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i),
static_cast<unsigned long>(shapes[i].mesh.tags.size()));
for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) {
printf(" tag[%ld] = %s ", static_cast<long>(t), shapes[i].mesh.tags[t].name.c_str()); printf(" tag[%ld] = %s ", static_cast<long>(t),
shapes[i].mesh.tags[t].name.c_str());
printf(" ints: ["); printf(" ints: [");
for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) {
{ printf("%ld", static_cast<long>(shapes[i].mesh.tags[t].intValues[j]));
printf("%ld", static_cast<long>(shapes[i].mesh.tags[t].intValues[j])); if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) {
if (j < (shapes[i].mesh.tags[t].intValues.size()-1)) printf(", ");
{ }
printf(", ");
}
} }
printf("]"); printf("]");
printf(" floats: ["); printf(" floats: [");
for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) {
{ printf("%f", static_cast<const double>(
printf("%f", static_cast<const double>(shapes[i].mesh.tags[t].floatValues[j])); shapes[i].mesh.tags[t].floatValues[j]));
if (j < (shapes[i].mesh.tags[t].floatValues.size()-1)) if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) {
{ printf(", ");
printf(", "); }
}
} }
printf("]"); printf("]");
printf(" strings: ["); printf(" strings: [");
for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) {
{ printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str());
printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) {
if (j < (shapes[i].mesh.tags[t].stringValues.size()-1)) printf(", ");
{ }
printf(", ");
}
} }
printf("]"); printf("]");
printf("\n"); printf("\n");
@@ -191,25 +199,60 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
} }
for (size_t i = 0; i < materials.size(); i++) { for (size_t i = 0; i < materials.size(); i++) {
printf("material[%ld].name = %s\n", static_cast<long>(i), materials[i].name.c_str()); printf("material[%ld].name = %s\n", static_cast<long>(i),
printf(" material.Ka = (%f, %f ,%f)\n", static_cast<const double>(materials[i].ambient[0]), static_cast<const double>(materials[i].ambient[1]), static_cast<const double>(materials[i].ambient[2])); materials[i].name.c_str());
printf(" material.Kd = (%f, %f ,%f)\n", static_cast<const double>(materials[i].diffuse[0]), static_cast<const double>(materials[i].diffuse[1]), static_cast<const double>(materials[i].diffuse[2])); printf(" material.Ka = (%f, %f ,%f)\n",
printf(" material.Ks = (%f, %f ,%f)\n", static_cast<const double>(materials[i].specular[0]), static_cast<const double>(materials[i].specular[1]), static_cast<const double>(materials[i].specular[2])); static_cast<const double>(materials[i].ambient[0]),
printf(" material.Tr = (%f, %f ,%f)\n", static_cast<const double>(materials[i].transmittance[0]), static_cast<const double>(materials[i].transmittance[1]), static_cast<const double>(materials[i].transmittance[2])); static_cast<const double>(materials[i].ambient[1]),
printf(" material.Ke = (%f, %f ,%f)\n", static_cast<const double>(materials[i].emission[0]), static_cast<const double>(materials[i].emission[1]), static_cast<const double>(materials[i].emission[2])); static_cast<const double>(materials[i].ambient[2]));
printf(" material.Ns = %f\n", static_cast<const double>(materials[i].shininess)); printf(" material.Kd = (%f, %f ,%f)\n",
static_cast<const double>(materials[i].diffuse[0]),
static_cast<const double>(materials[i].diffuse[1]),
static_cast<const double>(materials[i].diffuse[2]));
printf(" material.Ks = (%f, %f ,%f)\n",
static_cast<const double>(materials[i].specular[0]),
static_cast<const double>(materials[i].specular[1]),
static_cast<const double>(materials[i].specular[2]));
printf(" material.Tr = (%f, %f ,%f)\n",
static_cast<const double>(materials[i].transmittance[0]),
static_cast<const double>(materials[i].transmittance[1]),
static_cast<const double>(materials[i].transmittance[2]));
printf(" material.Ke = (%f, %f ,%f)\n",
static_cast<const double>(materials[i].emission[0]),
static_cast<const double>(materials[i].emission[1]),
static_cast<const double>(materials[i].emission[2]));
printf(" material.Ns = %f\n",
static_cast<const double>(materials[i].shininess));
printf(" material.Ni = %f\n", static_cast<const double>(materials[i].ior)); printf(" material.Ni = %f\n", static_cast<const double>(materials[i].ior));
printf(" material.dissolve = %f\n", static_cast<const double>(materials[i].dissolve)); printf(" material.dissolve = %f\n",
printf(" material.illum = %d\n", materials[i].illum); static_cast<const double>(materials[i].dissolve));
printf(" material.illum = %d\n", materials[i].illum);
printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str());
printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); printf(" material.map_Ns = %s\n",
materials[i].specular_highlight_texname.c_str());
printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str());
printf(" bump_multiplier = %f\n", static_cast<const double>(materials[i].bump_texopt.bump_multiplier));
printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str());
printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); printf(" material.disp = %s\n", materials[i].displacement_texname.c_str());
std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin()); printf(" <<PBR>>\n");
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end()); printf(" material.Pr = %f\n", static_cast<const double>(materials[i].roughness));
printf(" material.Pm = %f\n", static_cast<const double>(materials[i].metallic));
printf(" material.Ps = %f\n", static_cast<const double>(materials[i].sheen));
printf(" material.Pc = %f\n", static_cast<const double>(materials[i].clearcoat_thickness));
printf(" material.Pcr = %f\n", static_cast<const double>(materials[i].clearcoat_thickness));
printf(" material.aniso = %f\n", static_cast<const double>(materials[i].anisotropy));
printf(" material.anisor = %f\n", static_cast<const double>(materials[i].anisotropy_rotation));
printf(" material.map_Ke = %s\n", materials[i].emissive_texname.c_str());
printf(" material.map_Pr = %s\n", materials[i].roughness_texname.c_str());
printf(" material.map_Pm = %s\n", materials[i].metallic_texname.c_str());
printf(" material.map_Ps = %s\n", materials[i].sheen_texname.c_str());
printf(" material.norm = %s\n", materials[i].normal_texname.c_str());
std::map<std::string, std::string>::const_iterator it(
materials[i].unknown_parameter.begin());
std::map<std::string, std::string>::const_iterator itEnd(
materials[i].unknown_parameter.end());
for (; it != itEnd; it++) { for (; it != itEnd; it++) {
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
@@ -218,12 +261,8 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
} }
} }
static bool static bool TestLoadObj(const char* filename, const char* basepath = NULL,
TestLoadObj( bool triangulate = true) {
const char* filename,
const char* basepath = NULL,
bool triangulate = true)
{
std::cout << "Loading " << filename << std::endl; std::cout << "Loading " << filename << std::endl;
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
@@ -233,7 +272,8 @@ TestLoadObj(
timerutil t; timerutil t;
t.start(); t.start();
std::string err; std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate); bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
basepath, triangulate);
t.end(); t.end();
printf("Parsing time: %lu [msecs]\n", t.msec()); printf("Parsing time: %lu [msecs]\n", t.msec());
@@ -251,101 +291,95 @@ TestLoadObj(
return true; return true;
} }
static bool TestStreamLoadObj() {
static bool
TestStreamLoadObj()
{
std::cout << "Stream Loading " << std::endl; std::cout << "Stream Loading " << std::endl;
std::stringstream objStream; std::stringstream objStream;
objStream objStream << "mtllib cube.mtl\n"
<< "mtllib cube.mtl\n" "\n"
"\n" "v 0.000000 2.000000 2.000000\n"
"v 0.000000 2.000000 2.000000\n" "v 0.000000 0.000000 2.000000\n"
"v 0.000000 0.000000 2.000000\n" "v 2.000000 0.000000 2.000000\n"
"v 2.000000 0.000000 2.000000\n" "v 2.000000 2.000000 2.000000\n"
"v 2.000000 2.000000 2.000000\n" "v 0.000000 2.000000 0.000000\n"
"v 0.000000 2.000000 0.000000\n" "v 0.000000 0.000000 0.000000\n"
"v 0.000000 0.000000 0.000000\n" "v 2.000000 0.000000 0.000000\n"
"v 2.000000 0.000000 0.000000\n" "v 2.000000 2.000000 0.000000\n"
"v 2.000000 2.000000 0.000000\n" "# 8 vertices\n"
"# 8 vertices\n" "\n"
"\n" "g front cube\n"
"g front cube\n" "usemtl white\n"
"usemtl white\n" "f 1 2 3 4\n"
"f 1 2 3 4\n" "g back cube\n"
"g back cube\n" "# expects white material\n"
"# expects white material\n" "f 8 7 6 5\n"
"f 8 7 6 5\n" "g right cube\n"
"g right cube\n" "usemtl red\n"
"usemtl red\n" "f 4 3 7 8\n"
"f 4 3 7 8\n" "g top cube\n"
"g top cube\n" "usemtl white\n"
"usemtl white\n" "f 5 1 4 8\n"
"f 5 1 4 8\n" "g left cube\n"
"g left cube\n" "usemtl green\n"
"usemtl green\n" "f 5 6 2 1\n"
"f 5 6 2 1\n" "g bottom cube\n"
"g bottom cube\n" "usemtl white\n"
"usemtl white\n" "f 2 6 7 3\n"
"f 2 6 7 3\n" "# 6 elements";
"# 6 elements";
std::string matStream( std::string matStream(
"newmtl white\n" "newmtl white\n"
"Ka 0 0 0\n" "Ka 0 0 0\n"
"Kd 1 1 1\n" "Kd 1 1 1\n"
"Ks 0 0 0\n" "Ks 0 0 0\n"
"\n" "\n"
"newmtl red\n" "newmtl red\n"
"Ka 0 0 0\n" "Ka 0 0 0\n"
"Kd 1 0 0\n" "Kd 1 0 0\n"
"Ks 0 0 0\n" "Ks 0 0 0\n"
"\n" "\n"
"newmtl green\n" "newmtl green\n"
"Ka 0 0 0\n" "Ka 0 0 0\n"
"Kd 0 1 0\n" "Kd 0 1 0\n"
"Ks 0 0 0\n" "Ks 0 0 0\n"
"\n" "\n"
"newmtl blue\n" "newmtl blue\n"
"Ka 0 0 0\n" "Ka 0 0 0\n"
"Kd 0 0 1\n" "Kd 0 0 1\n"
"Ks 0 0 0\n" "Ks 0 0 0\n"
"\n" "\n"
"newmtl light\n" "newmtl light\n"
"Ka 20 20 20\n" "Ka 20 20 20\n"
"Kd 1 1 1\n" "Kd 1 1 1\n"
"Ks 0 0 0"); "Ks 0 0 0");
using namespace tinyobj; using namespace tinyobj;
class MaterialStringStreamReader: class MaterialStringStreamReader : public MaterialReader {
public MaterialReader public:
{ MaterialStringStreamReader(const std::string& matSStream)
public: : m_matSStream(matSStream) {}
MaterialStringStreamReader(const std::string& matSStream): m_matSStream(matSStream) {} virtual ~MaterialStringStreamReader() {}
virtual ~MaterialStringStreamReader() {} virtual bool operator()(const std::string& matId,
virtual bool operator() ( std::vector<material_t>* materials,
const std::string& matId, std::map<std::string, int>* matMap,
std::vector<material_t>* materials, std::string* err) {
std::map<std::string, int>* matMap, (void)matId;
std::string* err) (void)err;
{ LoadMtl(matMap, materials, &m_matSStream);
(void)matId; return true;
(void)err; }
LoadMtl(matMap, materials, &m_matSStream);
return true;
}
private: private:
std::stringstream m_matSStream; std::stringstream m_matSStream;
}; };
MaterialStringStreamReader matSSReader(matStream); MaterialStringStreamReader matSSReader(matStream);
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string err; std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader); bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream,
&matSSReader);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
@@ -360,11 +394,7 @@ std::string matStream(
return true; return true;
} }
int int main(int argc, char** argv) {
main(
int argc,
char **argv)
{
if (argc > 1) { if (argc > 1) {
const char* basepath = "models/"; const char* basepath = "models/";
if (argc > 2) { if (argc > 2) {
@@ -372,10 +402,11 @@ main(
} }
assert(true == TestLoadObj(argv[1], basepath)); assert(true == TestLoadObj(argv[1], basepath));
} else { } else {
//assert(true == TestLoadObj("cornell_box.obj")); // assert(true == TestLoadObj("cornell_box.obj"));
//assert(true == TestLoadObj("cube.obj")); // assert(true == TestLoadObj("cube.obj"));
assert(true == TestStreamLoadObj()); assert(true == TestStreamLoadObj());
assert(true == TestLoadObj("models/catmark_torus_creases0.obj", "models/", false)); assert(true ==
TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
} }
return 0; return 0;

6
models/issue-92.mtl Normal file
View File

@@ -0,0 +1,6 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
map_Kd tmp.png

7
models/issue-92.obj Normal file
View File

@@ -0,0 +1,7 @@
mtllib issue-92.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

5
models/issue-95-2.mtl Normal file
View File

@@ -0,0 +1,5 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Tf 0.1 0.2 0.3

7
models/issue-95-2.obj Normal file
View File

@@ -0,0 +1,7 @@
mtllib issue-95-2.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

5
models/issue-95.mtl Normal file
View File

@@ -0,0 +1,5 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3

7
models/issue-95.obj Normal file
View File

@@ -0,0 +1,7 @@
mtllib issue-95.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

19
models/pbr-mat-ext.mtl Normal file
View File

@@ -0,0 +1,19 @@
# .MTL with PBR extension.
newmtl pbr
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
Ke 0.1 0.1 0.1
Pr 0.2
Pm 0.3
Ps 0.4
Pc 0.5
Pcr 0.6
aniso 0.7
anisor 0.8
map_Pr roughness.tex
map_Pm metallic.tex
map_Ps sheen.tex
map_Ke emissive.tex
norm normalmap.tex

10
models/pbr-mat-ext.obj Normal file
View File

@@ -0,0 +1,10 @@
mtllib pbr-mat-ext.mtl
o floor
usemtl pbr
v 552.8 0.0 0.0
v 0.0 0.0 0.0
v 0.0 0.0 559.2
v 549.6 0.0 559.2
f 1 2 3 4

View File

@@ -0,0 +1,36 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
map_Ka -clamp on ambient.jpg
map_Kd -o 0.1 diffuse.jpg
map_Ks -s 0.1 0.2 specular.jpg
map_Ns -t 0.1 0.2 0.3 specular_highlight.jpg
map_bump -bm 3 bumpmap.jpg
newmtl bm2
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
# blendu
map_Kd -blendu on diffuse.jpg
map_Ks -blendv off specular.jpg
map_Ns -mm 0.1 0.3 specular_highlight.jpg
# -bm after filename
map_bump -imfchan r bumpmap2.jpg -bm 1.5
newmtl bm3
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
# type
map_Kd -type sphere diffuse.jpg
map_Ks -type cube_top specular.jpg
map_Ns -type cube_bottom specular_highlight.jpg
map_Ka -type cube_left ambient.jpg
map_d -type cube_right alpha.jpg
map_bump -type cube_front bump.jpg
disp -type cube_back displacement.jpg

View File

@@ -0,0 +1,7 @@
mtllib texture-options-issue-85.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

@@ -0,0 +1,30 @@
# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project.
# original cornell box data
# comment
# empty line including some space
mtllib cornell_box.mtl
o floor
v 552.8 0.0 0.0
v 0.0 0.0 0.0
v 0.0 0.0 559.2
v 549.6 0.0 559.2
v 130.0 0.0 65.0
v 82.0 0.0 225.0
v 240.0 0.0 272.0
v 290.0 0.0 114.0
v 423.0 0.0 247.0
v 265.0 0.0 296.0
v 314.0 0.0 456.0
v 472.0 0.0 406.0
f 1 2 3 4
f 8 7 6 5
f 12 11 10 9
usemtl white

3
python/TODO.md Normal file
View File

@@ -0,0 +1,3 @@
* PBR material
* Define index_t struct
* Python 2.7 binding

View File

@@ -1,160 +1,191 @@
//python3 module for tinyobjloader // python3 module for tinyobjloader
// //
//usage: // usage:
// import tinyobjloader as tol // import tinyobjloader as tol
// model = tol.LoadObj(name) // model = tol.LoadObj(name)
// print(model["shapes"]) // print(model["shapes"])
// print(model["materials"] // print(model["materials"]
// note:
// `shape.mesh.index_t` is represented as flattened array: (vertex_index, normal_index, texcoord_index) * num_faces
#include <Python.h> #include <Python.h>
#include <vector> #include <vector>
#include "../tiny_obj_loader.h" #include "../tiny_obj_loader.h"
typedef std::vector<double> vectd; typedef std::vector<double> vectd;
typedef std::vector<int> vecti;
PyObject* PyObject* pyTupleFromfloat3(float array[3]) {
pyTupleFromfloat3 (float array[3]) int i;
{ PyObject* tuple = PyTuple_New(3);
int i;
PyObject* tuple = PyTuple_New(3);
for(i=0; i<=2 ; i++){ for (i = 0; i <= 2; i++) {
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i])); PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
} }
return tuple; return tuple;
} }
extern "C" extern "C" {
{
static PyObject* static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
pyLoadObj(PyObject* self, PyObject* args) PyObject *rtndict, *pyshapes, *pymaterials, *attribobj, *current, *meshobj;
{
PyObject *rtndict, *pyshapes, *pymaterials,
*current, *meshobj;
char const* filename; char const* current_name;
char *current_name; char const* filename;
vectd vect; vectd vect;
std::vector<tinyobj::index_t> indices;
std::vector<unsigned char> face_verts;
std::vector<tinyobj::shape_t> shapes; tinyobj::attrib_t attrib;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
if(!PyArg_ParseTuple(args, "s", &filename)) if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
return NULL;
std::string err; std::string err;
tinyobj::LoadObj(shapes, materials, err, filename); tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
pyshapes = PyDict_New(); pyshapes = PyDict_New();
pymaterials = PyDict_New(); pymaterials = PyDict_New();
rtndict = PyDict_New(); rtndict = PyDict_New();
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin() ; attribobj = PyDict_New();
shape != shapes.end(); shape++)
{
meshobj = PyDict_New();
tinyobj::mesh_t cm = (*shape).mesh;
for (int i = 0; i <= 4; i++ ) for (int i = 0; i <= 2; i++) {
{ current = PyList_New(0);
current = PyList_New(0);
switch(i) { switch (i) {
case 0:
case 0: current_name = "vertices";
current_name = "positions"; vect = vectd(attrib.vertices.begin(), attrib.vertices.end());
vect = vectd(cm.positions.begin(), cm.positions.end()); break; break;
case 1: case 1:
current_name = "normals"; current_name = "normals";
vect = vectd(cm.normals.begin(), cm.normals.end()); break; vect = vectd(attrib.normals.begin(), attrib.normals.end());
case 2: break;
current_name = "texcoords"; case 2:
vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); break; current_name = "texcoords";
case 3: vect = vectd(attrib.texcoords.begin(), attrib.texcoords.end());
current_name = "indicies"; break;
vect = vectd(cm.indices.begin(), cm.indices.end()); break;
case 4:
current_name = "material_ids";
vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); break;
}
for (vectd::iterator it = vect.begin() ;
it != vect.end(); it++)
{
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
}
PyDict_SetItemString(meshobj, current_name, current);
}
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
} }
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin() ; for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
mat != materials.end(); mat++) PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
{
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()));
}
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()));
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, "unknown_parameter", unknown_parameter);
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
} }
PyDict_SetItemString(rtndict, "shapes", pyshapes); PyDict_SetItemString(attribobj, current_name, current);
PyDict_SetItemString(rtndict, "materials", pymaterials); }
return rtndict; 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));
}
PyDict_SetItemString(meshobj, "indices", 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]));
}
PyDict_SetItemString(meshobj, "num_face_vertices", 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]));
}
PyDict_SetItemString(meshobj, "material_ids", current);
}
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
}
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()));
}
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()));
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, "unknown_parameter", unknown_parameter);
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
}
PyDict_SetItemString(rtndict, "shapes", pyshapes);
PyDict_SetItemString(rtndict, "materials", pymaterials);
return rtndict;
} }
static PyMethodDef mMethods[] = { static PyMethodDef mMethods[] = {
{"LoadObj", pyLoadObj, METH_VARARGS}, {"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
{NULL, NULL, 0, NULL}
}; };
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
NULL, -1, mMethods};
static struct PyModuleDef moduledef = { PyMODINIT_FUNC PyInit_tinyobjloader(void) {
PyModuleDef_HEAD_INIT, return PyModule_Create(&moduledef);
"tinyobjloader",
NULL,
-1,
mMethods
};
PyMODINIT_FUNC
PyInit_tinyobjloader(void)
{
return PyModule_Create(&moduledef);
} }
} }

View File

@@ -184,6 +184,48 @@ TestLoadObj(
return true; return true;
} }
static bool
TestLoadObjFromPreopenedFile(
const char* filename,
const char* basepath = NULL,
bool readMaterials = true,
bool triangulate = true)
{
std::string fullFilename = std::string(basepath) + filename;
std::cout << "Loading " << fullFilename << std::endl;
std::ifstream fileStream(fullFilename.c_str());
if (!fileStream) {
std::cerr << "Could not find specified file: " << fullFilename << std::endl;
return false;
}
tinyobj::MaterialStreamReader materialStreamReader(fileStream);
tinyobj::MaterialStreamReader* materialReader = readMaterials
? &materialStreamReader
: NULL;
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &fileStream, materialReader);
if (!err.empty()) {
std::cerr << err << std::endl;
}
if (!ret) {
printf("Failed to load/parse .obj.\n");
return false;
}
std::cout << "Loaded material count: " << materials.size() << "\n";
return true;
}
static bool static bool
TestStreamLoadObj() TestStreamLoadObj()
@@ -293,7 +335,7 @@ std::string matStream(
return true; return true;
} }
const char* gMtlBasePath = "../models"; const char* gMtlBasePath = "../models/";
TEST_CASE("cornell_box", "[Loader]") { TEST_CASE("cornell_box", "[Loader]") {
@@ -319,10 +361,175 @@ TEST_CASE("catmark_torus_creases0", "[Loader]") {
REQUIRE(8 == shapes[0].mesh.tags.size()); REQUIRE(8 == shapes[0].mesh.tags.size());
} }
TEST_CASE("pbr", "[Loader]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/pbr-mat-ext.obj", gMtlBasePath, /*triangulate*/false);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
REQUIRE(0.2 == Approx(materials[0].roughness));
REQUIRE(0.3 == Approx(materials[0].metallic));
REQUIRE(0.4 == Approx(materials[0].sheen));
REQUIRE(0.5 == Approx(materials[0].clearcoat_thickness));
REQUIRE(0.6 == Approx(materials[0].clearcoat_roughness));
REQUIRE(0.7 == Approx(materials[0].anisotropy));
REQUIRE(0.8 == Approx(materials[0].anisotropy_rotation));
REQUIRE(0 == materials[0].roughness_texname.compare("roughness.tex"));
REQUIRE(0 == materials[0].metallic_texname.compare("metallic.tex"));
REQUIRE(0 == materials[0].sheen_texname.compare("sheen.tex"));
REQUIRE(0 == materials[0].emissive_texname.compare("emissive.tex"));
REQUIRE(0 == materials[0].normal_texname.compare("normalmap.tex"));
}
TEST_CASE("stream_load", "[Stream]") { TEST_CASE("stream_load", "[Stream]") {
REQUIRE(true == TestStreamLoadObj()); REQUIRE(true == TestStreamLoadObj());
} }
TEST_CASE("stream_load_from_file_skipping_materials", "[Stream]") {
REQUIRE(true == TestLoadObjFromPreopenedFile(
"../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/false, /*triangulate*/false));
}
TEST_CASE("stream_load_from_file_with_materials", "[Stream]") {
REQUIRE(true == TestLoadObjFromPreopenedFile(
"../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/true, /*triangulate*/false));
}
TEST_CASE("trailing_whitespace_in_mtl", "[Issue92]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-92.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
REQUIRE(0 == materials[0].diffuse_texname.compare("tmp.png"));
}
TEST_CASE("transmittance_filter", "[Issue95]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
}
TEST_CASE("transmittance_filter_Tf", "[Issue95-Tf]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95-2.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
}
TEST_CASE("transmittance_filter_Kt", "[Issue95-Kt]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
}
TEST_CASE("usemtl_at_last_line", "[Issue104]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/usemtl-issue-104.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == shapes.size());
}
TEST_CASE("texture_opts", "[Issue85]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/texture-options-issue-85.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == shapes.size());
REQUIRE(3 == materials.size());
REQUIRE(0 == materials[0].name.compare("default"));
REQUIRE(0 == materials[1].name.compare("bm2"));
REQUIRE(0 == materials[2].name.compare("bm3"));
REQUIRE(true == materials[0].ambient_texopt.clamp);
REQUIRE(0.1 == Approx(materials[0].diffuse_texopt.origin_offset[0]));
REQUIRE(0.0 == Approx(materials[0].diffuse_texopt.origin_offset[1]));
REQUIRE(0.0 == Approx(materials[0].diffuse_texopt.origin_offset[2]));
REQUIRE(0.1 == Approx(materials[0].specular_texopt.scale[0]));
REQUIRE(0.2 == Approx(materials[0].specular_texopt.scale[1]));
REQUIRE(1.0 == Approx(materials[0].specular_texopt.scale[2]));
REQUIRE(0.1 == Approx(materials[0].specular_highlight_texopt.turbulence[0]));
REQUIRE(0.2 == Approx(materials[0].specular_highlight_texopt.turbulence[1]));
REQUIRE(0.3 == Approx(materials[0].specular_highlight_texopt.turbulence[2]));
REQUIRE(3.0 == Approx(materials[0].bump_texopt.bump_multiplier));
REQUIRE(0.1 == Approx(materials[1].specular_highlight_texopt.brightness));
REQUIRE(0.3 == Approx(materials[1].specular_highlight_texopt.contrast));
REQUIRE('r' == materials[1].bump_texopt.imfchan);
REQUIRE(tinyobj::TEXTURE_TYPE_SPHERE == materials[2].diffuse_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_TOP == materials[2].specular_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM == materials[2].specular_highlight_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_LEFT == materials[2].ambient_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_RIGHT == materials[2].alpha_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_FRONT == materials[2].bump_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BACK == materials[2].displacement_texopt.type);
}
#if 0 #if 0
int int
main( main(

File diff suppressed because it is too large Load Diff