65 Commits
c89 ... v1.0.0

Author SHA1 Message Date
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
20 changed files with 1328 additions and 820 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,20 @@ 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
* 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 +67,14 @@ 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
Features ## Features
--------
* Group(parse multiple group name) * Group(parse multiple group name)
* Vertex * Vertex
@@ -80,20 +86,16 @@ Features
* 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.
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 +150,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 +164,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;

View File

@@ -1,15 +1,15 @@
// //
// 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 <string>
#include <cassert> #include <vector>
#include <algorithm>
#include <GL/glew.h> #include <GL/glew.h>
@@ -30,8 +30,8 @@
#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
@@ -45,7 +45,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 +81,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 +96,7 @@ public:
#endif #endif
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
DWORD t_[2]; DWORD t_[2];
#else #else
@@ -110,7 +110,7 @@ private:
}; };
typedef struct { typedef struct {
GLuint vb; // vertex buffer GLuint vb; // vertex buffer
int numTriangles; int numTriangles;
} DrawObject; } DrawObject;
@@ -161,8 +161,9 @@ 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,
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; std::vector<tinyobj::material_t> materials;
@@ -172,7 +173,8 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
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 +186,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);
@@ -196,91 +198,95 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
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]; float v[3][3];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3*f+1]; for (int k = 0; k < 3; k++) {
tinyobj::index_t idx2 = shapes[s].mesh.indices[3*f+2]; int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
float v[3][3]; int f2 = idx2.vertex_index;
for (int k = 0; k < 3; k++) { assert(f0 >= 0);
int f0 = idx0.vertex_index; assert(f1 >= 0);
int f1 = idx1.vertex_index; assert(f2 >= 0);
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);
}
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]);
} }
o.vb = 0; float n[3][3];
o.numTriangles = 0;
if (vb.size() > 0) { if (attrib.normals.size() > 0) {
glGenBuffers(1, &o.vb); int f0 = idx0.normal_index;
glBindBuffer(GL_ARRAY_BUFFER, o.vb); int f1 = idx1.normal_index;
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW); int f2 = idx2.normal_index;
o.numTriangles = vb.size() / 9 / 3; assert(f0 >= 0);
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles); 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];
} }
gDrawObjects.push_back(o); 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);
}
}
o.vb = 0;
o.numTriangles = 0;
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() / 9 / 3;
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
}
drawObjects->push_back(o);
} }
} }
@@ -290,8 +296,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,78 +312,90 @@ 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) {
{
glPolygonMode(GL_FRONT, GL_FILL); glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL); glPolygonMode(GL_BACK, GL_FILL);
@@ -396,8 +413,8 @@ void Draw(const std::vector<DrawObject>& drawObjects)
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 36, (const void*)0); glVertexPointer(3, GL_FLOAT, 36, (const void*)0);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float) * 3));
glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6)); glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float) * 6));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays"); CheckErrors("drawarrays");
@@ -420,7 +437,7 @@ void Draw(const std::vector<DrawObject>& drawObjects)
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 36, (const void*)0); glVertexPointer(3, GL_FLOAT, 36, (const void*)0);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float) * 3));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays"); CheckErrors("drawarrays");
@@ -443,9 +460,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 +468,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 +498,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, gDrawObjects, argv[1])) { if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, argv[1])) {
return -1; return -1;
} }
@@ -498,7 +510,7 @@ 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);
@@ -509,7 +521,8 @@ int main(int argc, char **argv)
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,7 +530,8 @@ 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);

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

@@ -214,11 +214,11 @@ 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;
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);
@@ -229,11 +229,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
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 = 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 +245,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 +272,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 +308,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 +339,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,24 +505,31 @@ 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]);
@@ -527,7 +540,9 @@ int main(int argc, char **argv)
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 +584,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,59 @@ 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(" 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", materials[i].roughness);
printf(" material.Pm = %f\n", materials[i].metallic);
printf(" material.Ps = %f\n", materials[i].sheen);
printf(" material.Pc = %f\n", materials[i].clearcoat_thickness);
printf(" material.Pcr = %f\n", materials[i].clearcoat_thickness);
printf(" material.aniso = %f\n", materials[i].anisotropy);
printf(" material.anisor = %f\n", 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 +260,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 +271,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 +290,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 +393,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 +401,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

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

@@ -293,7 +293,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 +319,107 @@ 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("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]));
}
#if 0 #if 0
int int
main( main(

View File

@@ -24,33 +24,6 @@ THE SOFTWARE.
// //
// version 1.0.0 : Change data structure. Change license from BSD to MIT. // version 1.0.0 : Change data structure. Change license from BSD to MIT.
// Support different index for
// vertex/normal/texcoord(#73, #39)
// version 0.9.20: Fixes creating per-face material using `usemtl`(#68)
// version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension)
// version 0.9.16: Make tinyobjloader header-only
// version 0.9.15: Change API to handle no mtl file case correctly(#58)
// version 0.9.14: Support specular highlight, bump, displacement and alpha
// map(#53)
// version 0.9.13: Report "Material file not found message" in `err`(#46)
// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before
// 'g' (#44)
// version 0.9.11: Invert `Tr` parameter(#43)
// version 0.9.10: Fix seg fault on windows.
// version 0.9.9 : Replace atof() with custom parser.
// version 0.9.8 : Fix multi-materials(per-face material ID).
// version 0.9.7 : Support multi-materials(per-face material ID) per
// object/group.
// version 0.9.6 : Support Ni(index of refraction) mtl parameter.
// Parse transmittance material parameter correctly.
// version 0.9.5 : Parse multiple group name.
// Add support of specifying the base path to load material
// file.
// version 0.9.4 : Initial support of group tag(g)
// version 0.9.3 : Fix parsing triple 'x/y/z'
// version 0.9.2 : Add more .mtl load support
// version 0.9.1 : Add initial .mtl load support
// version 0.9.0 : Initial
// //
// //
@@ -62,9 +35,9 @@ THE SOFTWARE.
#ifndef TINY_OBJ_LOADER_H_ #ifndef TINY_OBJ_LOADER_H_
#define TINY_OBJ_LOADER_H_ #define TINY_OBJ_LOADER_H_
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
namespace tinyobj { namespace tinyobj {
@@ -91,6 +64,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;
@@ -112,10 +101,11 @@ typedef struct {
typedef struct { typedef struct {
std::vector<index_t> indices; std::vector<index_t> indices;
std::vector<unsigned char> std::vector<unsigned char> num_face_vertices; // The number of vertices per
num_face_vertices; // The number of vertices per face. 3 = polygon, 4 = quad, ... Up to 255. // face. 3 = polygon, 4 = quad,
std::vector<int> material_ids; // per-face material ID // ... Up to 255.
std::vector<tag_t> tags; // SubD tag std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag
} mesh_t; } mesh_t;
typedef struct { typedef struct {
@@ -131,14 +121,22 @@ typedef struct {
} attrib_t; } attrib_t;
typedef struct callback_t_ { typedef struct callback_t_ {
void (*vertex_cb)(void *user_data, float x, float y, float z); // W is optional and set to 1 if there is no `w` item in `v` line
void (*vertex_cb)(void *user_data, float x, float y, float z, float 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);
void (*texcoord_cb)(void *user_data, float x, float y);
// -2147483648 will be passed for undefined index // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in
void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx); // `vt` line.
// `name` material name, `materialId` = the array index of material_t[]. -1 if void (*texcoord_cb)(void *user_data, float x, float y, float z);
// called per 'f' line. num_indices is the number of face indices(e.g. 3 for
// triangle, 4 for quad)
// 0 will be passed for undefined index in index_t members.
void (*index_cb)(void *user_data, index_t *indices, int num_indices);
// `name` material name, `material_id` = the array index of material_t[]. -1
// if
// a material not found in .mtl // a material not found in .mtl
void (*usemtl_cb)(void *user_data, const char *name, int materialId); void (*usemtl_cb)(void *user_data, const char *name, int material_id);
// `materials` = parsed material data. // `materials` = parsed material data.
void (*mtllib_cb)(void *user_data, const material_t *materials, void (*mtllib_cb)(void *user_data, const material_t *materials,
int num_materials); int num_materials);
@@ -155,7 +153,6 @@ typedef struct callback_t_ {
mtllib_cb(NULL), mtllib_cb(NULL),
group_cb(NULL), group_cb(NULL),
object_cb(NULL) {} object_cb(NULL) {}
} callback_t; } callback_t;
class MaterialReader { class MaterialReader {
@@ -200,12 +197,11 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
/// `callback.mtllib_cb`. /// `callback.mtllib_cb`.
/// Returns true when loading .obj/.mtl become success. /// Returns true when loading .obj/.mtl become success.
/// Returns warning and error message into `err` /// Returns warning and error message into `err`
/// 'mtl_basepath' is optional, and used for base path for .mtl file. /// See `examples/callback_api/` for how to use this function.
/// 'triangulate' is optional, and used whether triangulate polygon face in .obj bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
/// or not. void *user_data = NULL,
bool LoadObjWithCallback(void *user_data, const callback_t &callback, MaterialReader *readMatFn = NULL,
std::string *err, std::istream *inStream, std::string *err = NULL);
MaterialReader *readMatFn);
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
/// std::istream for materials. /// std::istream for materials.
@@ -223,12 +219,12 @@ void LoadMtl(std::map<std::string, int> *material_map,
} // namespace tinyobj } // namespace tinyobj
#ifdef TINYOBJLOADER_IMPLEMENTATION #ifdef TINYOBJLOADER_IMPLEMENTATION
#include <cstdlib>
#include <cstring>
#include <cassert> #include <cassert>
#include <cctype>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <cctype> #include <cstdlib>
#include <cstring>
#include <utility> #include <utility>
#include <fstream> #include <fstream>
@@ -261,6 +257,38 @@ struct obj_shape {
std::vector<float> vt; std::vector<float> vt;
}; };
// See
// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
static std::istream &safeGetline(std::istream &is, std::string &t) {
t.clear();
// The characters in the stream are read one-by-one using a std::streambuf.
// That is faster than reading them one-by-one using the std::istream.
// Code that uses streambuf this way must be guarded by a sentry object.
// The sentry object performs various tasks,
// such as thread synchronization and updating the stream state.
std::istream::sentry se(is, true);
std::streambuf *sb = is.rdbuf();
for (;;) {
int c = sb->sbumpc();
switch (c) {
case '\n':
return is;
case '\r':
if (sb->sgetc() == '\n') sb->sbumpc();
return is;
case EOF:
// Also handle the case when the last line has no line ending
if (t.empty()) is.setstate(std::ios::eofbit);
return is;
default:
t += static_cast<char>(c);
}
}
}
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
#define IS_DIGIT(x) \ #define IS_DIGIT(x) \
(static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10)) (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
@@ -424,18 +452,13 @@ fail:
return false; return false;
} }
static inline float parseFloat(const char **token) { static inline float parseFloat(const char **token, double default_value = 0.0) {
(*token) += strspn((*token), " \t"); (*token) += strspn((*token), " \t");
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
float f = static_cast<float>(atof(*token));
(*token) += strcspn((*token), " \t\r");
#else
const char *end = (*token) + strcspn((*token), " \t\r"); const char *end = (*token) + strcspn((*token), " \t\r");
double val = 0.0; double val = default_value;
tryParseDouble((*token), end, &val); tryParseDouble((*token), end, &val);
float f = static_cast<float>(val); float f = static_cast<float>(val);
(*token) = end; (*token) = end;
#endif
return f; return f;
} }
@@ -451,6 +474,14 @@ static inline void parseFloat3(float *x, float *y, float *z,
(*z) = parseFloat(token); (*z) = parseFloat(token);
} }
static inline void parseV(float *x, float *y, float *z, float *w,
const char **token) {
(*x) = parseFloat(token);
(*y) = parseFloat(token);
(*z) = parseFloat(token);
(*w) = parseFloat(token, 1.0);
}
static tag_sizes parseTagTriple(const char **token) { static tag_sizes parseTagTriple(const char **token) {
tag_sizes ts; tag_sizes ts;
@@ -510,8 +541,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize,
// 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 vertex_index parseRawTriple(const char **token) {
vertex_index vi( vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
vi.v_idx = atoi((*token)); vi.v_idx = atoi((*token));
(*token) += strcspn((*token), "/ \t\r"); (*token) += strcspn((*token), "/ \t\r");
@@ -562,6 +592,20 @@ static void InitMaterial(material_t *material) {
material->dissolve = 1.f; material->dissolve = 1.f;
material->shininess = 1.f; material->shininess = 1.f;
material->ior = 1.f; material->ior = 1.f;
material->roughness = 0.f;
material->metallic = 0.f;
material->sheen = 0.f;
material->clearcoat_thickness = 0.f;
material->clearcoat_roughness = 0.f;
material->anisotropy_rotation = 0.f;
material->anisotropy = 0.f;
material->roughness_texname = "";
material->metallic_texname = "";
material->sheen_texname = "";
material->emissive_texname = "";
material->normal_texname = "";
material->unknown_parameter.clear(); material->unknown_parameter.clear();
} }
@@ -616,7 +660,8 @@ static bool exportFaceGroupToShape(
shape->mesh.indices.push_back(idx); shape->mesh.indices.push_back(idx);
} }
shape->mesh.num_face_vertices.push_back(static_cast<unsigned char>(npolys)); shape->mesh.num_face_vertices.push_back(
static_cast<unsigned char>(npolys));
shape->mesh.material_ids.push_back(material_id); // per face shape->mesh.material_ids.push_back(material_id); // per face
} }
} }
@@ -633,12 +678,15 @@ void LoadMtl(std::map<std::string, int> *material_map,
material_t material; material_t material;
InitMaterial(&material); InitMaterial(&material);
size_t maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size.
while (inStream->peek() != -1) { while (inStream->peek() != -1) {
inStream->getline(&buf[0], static_cast<std::streamsize>(maxchars)); std::string linebuf;
std::string linebuf(&buf[0]); safeGetline(*inStream, linebuf);
// 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) {
@@ -722,7 +770,8 @@ 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);
@@ -777,6 +826,55 @@ 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;
@@ -833,6 +931,41 @@ 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) {
@@ -926,12 +1059,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
shape_t shape; shape_t shape;
int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
while (inStream->peek() != -1) { while (inStream->peek() != -1) {
inStream->getline(&buf[0], maxchars); std::string linebuf;
safeGetline(*inStream, linebuf);
std::string linebuf(&buf[0]);
// Trim newline '\r\n' or '\n' // Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) { if (linebuf.size() > 0) {
@@ -1193,21 +1323,26 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
return true; return true;
} }
bool LoadObjWithCallback(void *user_data, const callback_t &callback, bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
std::string *err, std::istream *inStream, void *user_data /*= NULL*/,
MaterialReader *readMatFn) { MaterialReader *readMatFn /*= NULL*/,
std::string *err /*= NULL*/) {
std::stringstream errss; std::stringstream errss;
// material // material
std::map<std::string, int> material_map; std::map<std::string, int> material_map;
int materialId = -1; // -1 = invalid int material_id = -1; // -1 = invalid
int maxchars = 8192; // Alloc enough size. std::vector<index_t> indices;
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size. std::vector<material_t> materials;
while (inStream->peek() != -1) { std::vector<std::string> names;
inStream->getline(&buf[0], maxchars); names.reserve(2);
std::string name;
std::vector<const char *> names_out;
std::string linebuf(&buf[0]); std::string linebuf;
while (inStream.peek() != -1) {
safeGetline(inStream, linebuf);
// Trim newline '\r\n' or '\n' // Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) { if (linebuf.size() > 0) {
@@ -1236,10 +1371,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
// 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, y, z, w; // w is optional. default = 1.0
parseFloat3(&x, &y, &z, &token); parseV(&x, &y, &z, &w, &token);
if (callback.vertex_cb) { if (callback.vertex_cb) {
callback.vertex_cb(user_data, x, y, z); callback.vertex_cb(user_data, x, y, z, w);
} }
continue; continue;
} }
@@ -1258,10 +1393,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
// 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, y, z; // y and z are optional. default = 0.0
parseFloat2(&x, &y, &token); parseFloat3(&x, &y, &z, &token);
if (callback.texcoord_cb) { if (callback.texcoord_cb) {
callback.texcoord_cb(user_data, x, y); callback.texcoord_cb(user_data, x, y, z);
} }
continue; continue;
} }
@@ -1271,15 +1406,25 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
token += 2; token += 2;
token += strspn(token, " \t"); token += strspn(token, " \t");
indices.clear();
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
vertex_index vi = parseRawTriple(&token); vertex_index vi = parseRawTriple(&token);
if (callback.index_cb) {
callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx); index_t idx;
} idx.vertex_index = vi.v_idx;
idx.normal_index = vi.vn_idx;
idx.texcoord_index = vi.vt_idx;
indices.push_back(idx);
size_t n = strspn(token, " \t\r"); size_t n = strspn(token, " \t\r");
token += n; token += n;
} }
if (callback.index_cb && indices.size() > 0) {
callback.index_cb(user_data, &indices.at(0),
static_cast<int>(indices.size()));
}
continue; continue;
} }
@@ -1288,7 +1433,8 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7; token += 7;
#ifdef _MSC_VER #ifdef _MSC_VER
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); sscanf_s(token, "%s", namebuf,
static_cast<unsigned int>(_countof(namebuf)));
#else #else
sscanf(token, "%s", namebuf); sscanf(token, "%s", namebuf);
#endif #endif
@@ -1300,12 +1446,12 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
// { error!! material not found } // { error!! material not found }
} }
if (newMaterialId != materialId) { if (newMaterialId != material_id) {
materialId = newMaterialId; material_id = newMaterialId;
} }
if (callback.usemtl_cb) { if (callback.usemtl_cb) {
callback.usemtl_cb(user_data, namebuf, materialId); callback.usemtl_cb(user_data, namebuf, material_id);
} }
continue; continue;
@@ -1313,28 +1459,30 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
// load mtl // load mtl
if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; if (readMatFn) {
token += 7; char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7;
#ifdef _MSC_VER #ifdef _MSC_VER
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
#else #else
sscanf(token, "%s", namebuf); sscanf(token, "%s", namebuf);
#endif #endif
std::string err_mtl; std::string err_mtl;
std::vector<material_t> materials; materials.clear();
bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl); bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
if (err) { if (err) {
(*err) += err_mtl; (*err) += err_mtl;
} }
if (!ok) { if (!ok) {
return false; return false;
} }
if (callback.mtllib_cb) { if (callback.mtllib_cb) {
callback.mtllib_cb(user_data, &materials.at(0), callback.mtllib_cb(user_data, &materials.at(0),
static_cast<int>(materials.size())); static_cast<int>(materials.size()));
}
} }
continue; continue;
@@ -1342,8 +1490,7 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
// group name // group name
if (token[0] == 'g' && IS_SPACE((token[1]))) { if (token[0] == 'g' && IS_SPACE((token[1]))) {
std::vector<std::string> names; names.clear();
names.reserve(2);
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
std::string str = parseString(&token); std::string str = parseString(&token);
@@ -1353,51 +1500,49 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
assert(names.size() > 0); assert(names.size() > 0);
std::string name;
// names[0] must be 'g', so skip the 0th element. // names[0] must be 'g', so skip the 0th element.
if (names.size() > 1) { if (names.size() > 1) {
name = names[1]; name = names[1];
} else { } else {
name = ""; name.clear();
} }
if (callback.group_cb) { if (callback.group_cb) {
if (names.size() > 1) { if (names.size() > 1) {
// create const char* array. // create const char* array.
std::vector<const char *> tmp(names.size() - 1); names_out.resize(names.size() - 1);
for (size_t j = 0; j < tmp.size(); j++) { for (size_t j = 0; j < names_out.size(); j++) {
tmp[j] = names[j + 1].c_str(); names_out[j] = names[j + 1].c_str();
} }
callback.group_cb(user_data, &tmp.at(0), callback.group_cb(user_data, &names_out.at(0),
static_cast<int>(tmp.size())); static_cast<int>(names_out.size()));
} else { } else {
callback.group_cb(user_data, NULL, 0); callback.group_cb(user_data, NULL, 0);
} }
} }
continue; continue;
} }
// object name // object name
if (token[0] == 'o' && IS_SPACE((token[1]))) { if (token[0] == 'o' && IS_SPACE((token[1]))) {
// @todo { multiple object name? } // @todo { multiple object name? }
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 2; token += 2;
#ifdef _MSC_VER #ifdef _MSC_VER
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
#else #else
sscanf(token, "%s", namebuf); sscanf(token, "%s", namebuf);
#endif #endif
std::string name = std::string(namebuf); std::string object_name = std::string(namebuf);
if (callback.object_cb) { if (callback.object_cb) {
callback.object_cb(user_data, name.c_str()); callback.object_cb(user_data, object_name.c_str());
} }
continue; continue;
} }
#if 0 // @todo #if 0 // @todo
if (token[0] == 't' && IS_SPACE(token[1])) { if (token[0] == 't' && IS_SPACE(token[1])) {