6 Commits
v1.0.0 ... c89

Author SHA1 Message Date
Syoyo Fujita
c006401ea4 Finish initial implementation of .obj parser in C89. 2016-07-15 21:00:58 +09:00
Syoyo Fujita
3a96dc11ca Construct shape information. 2016-07-15 19:46:36 +09:00
Syoyo Fujita
7a9a0e7cab Update C89 parser(still W.I.P.). 2016-07-15 12:55:41 +09:00
Syoyo Fujita
0d1c60aafd Mostly finished porting parser in C89 except for material loading. 2016-07-15 01:47:01 +09:00
Syoyo Fujita
5f76db8498 Implement more c89 version(stil W.I.P.) 2016-07-14 20:03:07 +09:00
Syoyo Fujita
082d051c86 Experimental C89 version(W.I.P.). 2016-07-14 15:30:14 +09:00
23 changed files with 2729 additions and 1323 deletions

View File

@@ -1,4 +1,5 @@
# 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)
@@ -16,24 +17,29 @@ 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 .
Notice! What's new
----------
* 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.
@@ -42,20 +48,12 @@ 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
@@ -67,14 +65,10 @@ 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
* voxelizer https://github.com/karimnaaji/voxelizer * Your project here!
* 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
@@ -86,16 +80,20 @@ TinyObjLoader is successfully used in ...
* 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`.
@@ -150,7 +148,8 @@ 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.
@@ -164,6 +163,7 @@ 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: 1.0.{build} version: 0.9.{build}
platform: x64 platform: x64
@@ -7,6 +7,7 @@ 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,21 +1,15 @@
//
// 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 <fstream> #include <cassert>
#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;
@@ -27,18 +21,19 @@ typedef struct {
} MyMesh; } MyMesh;
void vertex_cb(void *user_data, float x, float y, float z, float w) { void vertex_cb(void *user_data, float x, float y, float z)
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); {
printf("v[%ld] = %f, %f, %f (w %f)\n", mesh->vertices.size() / 3, x, y, z, w); MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
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);
@@ -46,56 +41,49 @@ 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, float z) { void texcoord_cb(void *user_data, float x, float y)
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); {
printf("vt[%ld] = %f, %f, %f\n", mesh->texcoords.size() / 3, x, y, z); MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
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, tinyobj::index_t *indices, int num_indices) { void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx)
{
// 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(whic means relative // (e.g. v_indices.size()) when the value is negative(relative index).
// 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, 0 is set for the index value which // Also, -2147483648(0x80000000) is set for the index value which does not exist in .obj
// does not exist in .obj MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), v_idx, vn_idx, vt_idx);
for (int i = 0; i < num_indices; i++) { if (v_idx != 0x80000000) {
tinyobj::index_t idx = indices[i]; mesh->v_indices.push_back(v_idx);
printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), idx.vertex_index,
idx.normal_index, idx.texcoord_index);
if (idx.vertex_index != 0) {
mesh->v_indices.push_back(idx.vertex_index);
} }
if (idx.normal_index != 0) { if (vn_idx != 0x80000000) {
mesh->vn_indices.push_back(idx.normal_index); mesh->vn_indices.push_back(vn_idx);
}
if (idx.texcoord_index != 0) {
mesh->vt_indices.push_back(idx.texcoord_index);
} }
if (vt_idx != 0x80000000) {
mesh->vt_indices.push_back(vt_idx);
} }
} }
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, printf("usemtl. material id = %d(name = %s)\n", material_idx, mesh->materials[material_idx].name.c_str());
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, void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_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++) {
@@ -103,8 +91,9 @@ void mtllib_cb(void *user_data, const tinyobj::material_t *materials,
} }
} }
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++) {
@@ -112,12 +101,16 @@ 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 main(int argc, char **argv) { int
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;
@@ -130,11 +123,7 @@ int main(int argc, char **argv) {
MyMesh mesh; MyMesh mesh;
std::string err; std::string err;
std::string filename = "../../models/cornell_box.obj"; std::ifstream ifs("../../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;
@@ -143,7 +132,7 @@ int main(int argc, char **argv) {
tinyobj::MaterialFileReader mtlReader("../../models/"); tinyobj::MaterialFileReader mtlReader("../../models/");
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err); bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader);
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 <algorithm> #include <vector>
#include <cassert> #include <string>
#include <cmath>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <string> #include <cmath>
#include <vector> #include <cassert>
#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 <mmsystem.h>
#include <windows.h> #include <windows.h>
#include <mmsystem.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;
@@ -96,7 +96,7 @@ class timerutil {
#endif #endif
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
DWORD t_[2]; DWORD t_[2];
#else #else
@@ -161,9 +161,8 @@ void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
} }
} }
bool LoadObjAndConvert(float bmin[3], float bmax[3], bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& drawObjects, const char* filename)
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;
@@ -173,8 +172,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
tm.start(); tm.start();
std::string err; std::string err;
bool ret = bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
} }
@@ -186,7 +184,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
return false; return false;
} }
printf("Parsing time: %d [ms]\n", (int)tm.msec()); printf("Parsing time: %d [ms]\n", 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);
@@ -201,10 +199,11 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
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 idx0 = shapes[s].mesh.indices[3*f+0];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2]; tinyobj::index_t idx1 = shapes[s].mesh.indices[3*f+1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3*f+2];
float v[3][3]; float v[3][3];
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
@@ -215,9 +214,9 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
assert(f1 >= 0); assert(f1 >= 0);
assert(f2 >= 0); assert(f2 >= 0);
v[0][k] = attrib.vertices[3 * f0 + k]; v[0][k] = attrib.vertices[3*f0+k];
v[1][k] = attrib.vertices[3 * f1 + k]; v[1][k] = attrib.vertices[3*f1+k];
v[2][k] = attrib.vertices[3 * f2 + k]; v[2][k] = attrib.vertices[3*f2+k];
bmin[k] = std::min(v[0][k], bmin[k]); bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]); bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]); bmin[k] = std::min(v[2][k], bmin[k]);
@@ -236,19 +235,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
assert(f1 >= 0); assert(f1 >= 0);
assert(f2 >= 0); assert(f2 >= 0);
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3 * f0 + k]; n[0][k] = attrib.normals[3*f0+k];
n[1][k] = attrib.normals[3 * f1 + k]; n[1][k] = attrib.normals[3*f1+k];
n[2][k] = attrib.normals[3 * f2 + k]; n[2][k] = attrib.normals[3*f2+k];
} }
} else { } else {
// compute geometric normal // compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]); CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0]; n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2];
n[1][1] = n[0][1]; n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2];
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++) { for (int k = 0; k < 3; k++) {
@@ -272,6 +267,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
vb.push_back(c[1] * 0.5 + 0.5); vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5); vb.push_back(c[2] * 0.5 + 0.5);
} }
} }
o.vb = 0; o.vb = 0;
@@ -279,14 +275,12 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
if (vb.size() > 0) { if (vb.size() > 0) {
glGenBuffers(1, &o.vb); glGenBuffers(1, &o.vb);
glBindBuffer(GL_ARRAY_BUFFER, o.vb); glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW);
GL_STATIC_DRAW);
o.numTriangles = vb.size() / 9 / 3; o.numTriangles = vb.size() / 9 / 3;
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles);
o.numTriangles);
} }
drawObjects->push_back(o); gDrawObjects.push_back(o);
} }
} }
@@ -296,7 +290,8 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
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);
@@ -312,69 +307,56 @@ void reshapeFunc(GLFWwindow* window, int w, int h) {
height = h; height = h;
} }
void keyboardFunc(GLFWwindow* window, int key, int scancode, int action, void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) {
int mods) { if(action == GLFW_PRESS || action == GLFW_REPEAT){
(void)window;
(void)scancode;
(void)mods;
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
// Move camera // Move camera
float mv_x = 0, mv_y = 0, mv_z = 0; float mv_x = 0, mv_y = 0, mv_z = 0;
if (key == GLFW_KEY_K) if(key == GLFW_KEY_K) mv_x += 1;
mv_x += 1; else if(key == GLFW_KEY_J) mv_x += -1;
else if (key == GLFW_KEY_J) else if(key == GLFW_KEY_L) mv_y += 1;
mv_x += -1; else if(key == GLFW_KEY_H) mv_y += -1;
else if (key == GLFW_KEY_L) else if(key == GLFW_KEY_P) mv_z += 1;
mv_y += 1; else if(key == GLFW_KEY_N) mv_z += -1;
else if (key == GLFW_KEY_H) //camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
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 // Close window
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE);
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){
(void)window; if(button == GLFW_MOUSE_BUTTON_LEFT){
(void)mods; if(action == GLFW_PRESS){
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
mouseLeftPressed = true; mouseLeftPressed = true;
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
} else if (action == GLFW_RELEASE) { } else if(action == GLFW_RELEASE){
mouseLeftPressed = false; mouseLeftPressed = false;
} }
} }
if (button == GLFW_MOUSE_BUTTON_RIGHT) { if(button == GLFW_MOUSE_BUTTON_RIGHT){
if (action == GLFW_PRESS) { if(action == GLFW_PRESS){
mouseRightPressed = true; mouseRightPressed = true;
} else if (action == GLFW_RELEASE) { } else if(action == GLFW_RELEASE){
mouseRightPressed = false; mouseRightPressed = false;
} }
} }
if (button == GLFW_MOUSE_BUTTON_MIDDLE) { if(button == GLFW_MOUSE_BUTTON_MIDDLE){
if (action == GLFW_PRESS) { if(action == GLFW_PRESS){
mouseMiddlePressed = true; mouseMiddlePressed = true;
} else if (action == GLFW_RELEASE) { } else if(action == GLFW_RELEASE){
mouseMiddlePressed = false; 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, rotScale * (2.0f * prevMouseX - width) / (float)width, trackball(prev_quat,
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);
@@ -395,7 +377,8 @@ void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
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);
@@ -413,8 +396,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");
@@ -437,7 +420,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");
@@ -460,7 +443,9 @@ 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;
@@ -468,13 +453,16 @@ 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;
@@ -498,7 +486,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;
} }
@@ -510,7 +498,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);
@@ -521,8 +509,7 @@ 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], gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]);
up[1], up[2]);
build_rotmatrix(mat, curr_quat); build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]); glMultMatrixf(&mat[0][0]);
@@ -530,8 +517,7 @@ 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]), glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2]));
-0.5 * (bmax[2] + bmin[2]));
Draw(gDrawObjects); Draw(gDrawObjects);

12
experimental/Makefile Normal file
View File

@@ -0,0 +1,12 @@
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
LD_FLAGS=-framework OpenGL -lglfw3 -lglew
endif
ifeq ($(UNAME_S),Linux)
LD_FLAGS=-lGL -lGLU -lglfw3 -lGLEW -lX11 -lXrandr -lXinerama -lXxf86vm -lXcursor -lm -pthread -ldl
endif
all:
clang -c trackball.c
clang -o viewer -g -O0 -pedantic -ansi -Wno-deprecated-declarations viewer-c.c trackball.o $(LD_FLAGS)
# clang -pedantic -ansi -Weverything -Wno-deprecated-declarations viewer-c.c trackball.o -framework OpenGL -lglfw3 -lglew

File diff suppressed because it is too large Load Diff

View File

@@ -292,22 +292,6 @@ 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;
@@ -317,20 +301,19 @@ typedef struct {
unsigned int length; unsigned int length;
} shape_t; } shape_t;
struct index_t { struct vertex_index {
int vertex_index, texcoord_index, normal_index; int v_idx, vt_idx, vn_idx;
index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {} vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
explicit index_t(int idx) explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
: vertex_index(idx), texcoord_index(idx), normal_index(idx) {} vertex_index(int vidx, int vtidx, int vnidx)
index_t(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(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<index_t, lt::allocator<index_t> > indices; std::vector<vertex_index, lt::allocator<vertex_index> > faces;
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;
@@ -403,11 +386,12 @@ 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 index_t parseRawTriple(const char **token) { static vertex_index parseRawTriple(const char **token) {
index_t vi( vertex_index vi(
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
vi.vertex_index = my_atoi((*token)); vi.v_idx = 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)++;
@@ -420,7 +404,8 @@ static index_t parseRawTriple(const char **token) {
// i//k // i//k
if ((*token)[0] == '/') { if ((*token)[0] == '/') {
(*token)++; (*token)++;
vi.normal_index = my_atoi((*token)); vi.vn_idx = 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)++;
@@ -429,7 +414,8 @@ static index_t parseRawTriple(const char **token) {
} }
// i/j/k or i/j // i/j/k or i/j
vi.texcoord_index = my_atoi((*token)); vi.vt_idx = 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)++;
@@ -440,7 +426,8 @@ static index_t parseRawTriple(const char **token) {
// i/j/k // i/j/k
(*token)++; // skip '/' (*token)++; // skip '/'
vi.normal_index = my_atoi((*token)); vi.vn_idx = 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)++;
@@ -449,17 +436,17 @@ static index_t 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); skip_space(token); //(*token) += strspn((*token), " \t");
size_t e = until_space((*token)); size_t e = until_space((*token)); // strcspn((*token), " \t\r");
(*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); skip_space(token); //(*token) += strspn((*token), " \t");
int i = my_atoi((*token)); int i = my_atoi((*token));
(*token) += until_space((*token)); (*token) += until_space((*token)); // strcspn((*token), " \t\r");
return i; return i;
} }
@@ -598,9 +585,9 @@ assemble :
{ {
// = pow(5.0, exponent); // = pow(5.0, exponent);
double a = 1.0; double a = 5.0;
for (int i = 0; i < exponent; i++) { for (int i = 0; i < exponent; i++) {
a = a * 5.0; a = a * a;
} }
*result = *result =
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); //(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
@@ -614,12 +601,13 @@ fail:
} }
static inline float parseFloat(const char **token) { static inline float parseFloat(const char **token) {
skip_space(token); skip_space(token); //(*token) += strspn((*token), " \t");
#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 = (*token) + until_space((*token)); const char *end =
(*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);
@@ -677,11 +665,6 @@ 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')
@@ -764,8 +747,7 @@ 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);
@@ -813,7 +795,6 @@ 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])
@@ -821,55 +802,6 @@ 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;
@@ -926,41 +858,6 @@ 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) {
@@ -999,8 +896,8 @@ typedef struct {
float tx, ty; float tx, ty;
// for f // for f
std::vector<index_t, lt::allocator<index_t> > f; std::vector<vertex_index, lt::allocator<vertex_index> > f;
// std::vector<index_t> f; // std::vector<vertex_index> 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;
@@ -1021,42 +918,44 @@ 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_indices; size_t num_faces;
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_indices = 0; num_faces = 0;
} }
}; };
class LoadOption { class
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; int req_num_threads;
bool triangulate; bool triangulate;
bool verbose; 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, bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
std::vector<material_t> *materials, const char *buf, size_t len, size_t len, const LoadOption& option);
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);
memcpy(linebuf, p, p_len); // StackVector<char, 256> linebuf;
// 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;
@@ -1064,7 +963,8 @@ 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.
skip_space(&token); // token += strspn(token, " \t");
skip_space(&token); //(*token) += strspn((*token), " \t");
assert(token); assert(token);
if (token[0] == '\0') { // empty line if (token[0] == '\0') { // empty line
@@ -1078,7 +978,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 = 0.0f, y = 0.0f, z = 0.0f; float x, y, z;
parseFloat3(&x, &y, &z, &token); parseFloat3(&x, &y, &z, &token);
command->vx = x; command->vx = x;
command->vy = y; command->vy = y;
@@ -1090,7 +990,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 = 0.0f, y = 0.0f, z = 0.0f; float x, y, z;
parseFloat3(&x, &y, &z, &token); parseFloat3(&x, &y, &z, &token);
command->nx = x; command->nx = x;
command->ny = y; command->ny = y;
@@ -1102,7 +1002,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 = 0.0f, y = 0.0f; float x, y;
parseFloat2(&x, &y, &token); parseFloat2(&x, &y, &token);
command->tx = x; command->tx = x;
command->ty = y; command->ty = y;
@@ -1113,12 +1013,19 @@ 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<index_t, 8> f; StackVector<vertex_index, 8> f;
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
index_t vi = parseRawTriple(&token); vertex_index 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);
@@ -1127,9 +1034,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) {
index_t i0 = f[0]; vertex_index i0 = f[0];
index_t i1(-1); vertex_index i1(-1);
index_t i2 = f[1]; vertex_index 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;
@@ -1246,21 +1153,20 @@ 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, bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
std::vector<material_t> *materials, const char *buf, size_t len, size_t len, const LoadOption& option)
const LoadOption &option) { {
attrib->vertices.clear(); attrib->vertices.clear();
attrib->normals.clear(); attrib->normals.clear();
attrib->texcoords.clear(); attrib->texcoords.clear();
attrib->indices.clear(); attrib->faces.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) auto num_threads = (option.req_num_threads < 0) ? std::thread::hardware_concurrency()
? std::thread::hardware_concurrency()
: option.req_num_threads; : 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));
@@ -1398,7 +1304,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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_indices += command.f_num_verts.size(); command_count[t].num_faces++;
} }
if (command.type == COMMAND_MTLLIB) { if (command.type == COMMAND_MTLLIB) {
@@ -1423,6 +1329,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
} }
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 &&
@@ -1437,7 +1344,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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;
@@ -1461,15 +1368,14 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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_indices = 0; size_t num_faces = 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_indices += command_count[t].num_indices; num_faces += command_count[t].num_faces;
} }
// 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;
@@ -1483,9 +1389,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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->indices.resize(num_f); attrib->faces.resize(num_f);
attrib->face_num_verts.resize(num_indices); attrib->face_num_verts.resize(num_faces);
attrib->material_ids.resize(num_indices); attrib->material_ids.resize(num_faces);
size_t v_offsets[kMaxThreads]; size_t v_offsets[kMaxThreads];
size_t n_offsets[kMaxThreads]; size_t n_offsets[kMaxThreads];
@@ -1504,7 +1410,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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_indices; face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_faces;
} }
StackVector<std::thread, 16> workers; StackVector<std::thread, 16> workers;
@@ -1550,20 +1456,17 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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++) {
index_t &vi = commands[t][i].f[k]; vertex_index &vi = commands[t][i].f[k];
int vertex_index = fixIndex(vi.vertex_index, v_count); int v_idx = fixIndex(vi.v_idx, v_count);
int texcoord_index = fixIndex(vi.texcoord_index, t_count); int vn_idx = fixIndex(vi.vn_idx, n_count);
int normal_index = fixIndex(vi.normal_index, n_count); int vt_idx = fixIndex(vi.vt_idx, t_count);
attrib->indices[f_count + k] = attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
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 += commands[t][i].f_num_verts.size(); face_count++;
} }
} }
})); }));
@@ -1664,8 +1567,8 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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->indices.size() << std::endl; std::cout << "# of face indices = " << attrib->faces.size() << std::endl;
std::cout << "# of indices = " << attrib->material_ids.size() << std::endl; std::cout << "# of faces = " << attrib->material_ids.size() << std::endl;
std::cout << "# of shapes = " << shapes->size() << std::endl; std::cout << "# of shapes = " << shapes->size() << std::endl;
} }

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) {
int i; register int i;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
v2[i] = v1[i]; v2[i] = v1[i];
} }

622
experimental/viewer-c.c Normal file
View File

@@ -0,0 +1,622 @@
#include <GL/glew.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#ifdef __APPLE__
#include <OpenGL/glu.h>
#else
#include <GL/glu.h>
#endif
#include <GLFW/glfw3.h>
#include "trackball.h"
#define TINYOBJ_LOADER_C_IMPLEMENTATION
#include "tinyobj_loader_c.h"
#define MAX_OBJECTS (128)
typedef struct {
GLuint vb;
int numTriangles;
} DrawObject;
static DrawObject gDrawObject;
static int width = 768;
static int height = 768;
static float prevMouseX, prevMouseY;
static int mouseLeftPressed;
static int mouseMiddlePressed;
static int mouseRightPressed;
static float curr_quat[4];
static float prev_quat[4];
static float eye[3], lookat[3], up[3];
static GLFWwindow* gWindow;
static void CheckErrors(const char* desc) {
GLenum e = glGetError();
if (e != GL_NO_ERROR) {
fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc, e, e);
exit(20);
}
}
static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
float v10[3];
float v20[3];
float len2;
v10[0] = v1[0] - v0[0];
v10[1] = v1[1] - v0[1];
v10[2] = v1[2] - v0[2];
v20[0] = v2[0] - v0[0];
v20[1] = v2[1] - v0[1];
v20[2] = v2[2] - v0[2];
N[0] = v20[1] * v10[2] - v20[2] * v10[1];
N[1] = v20[2] * v10[0] - v20[0] * v10[2];
N[2] = v20[0] * v10[1] - v20[1] * v10[0];
len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2];
if (len2 > 0.0f) {
float len = sqrt(len2);
N[0] /= len;
N[1] /= len;
}
}
static const char* mmap_file(size_t* len, const char* filename) {
#ifdef _WIN64
HANDLE file =
CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
assert(file != INVALID_HANDLE_VALUE);
HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
assert(fileMapping != INVALID_HANDLE_VALUE);
LPVOID fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0);
auto fileMapViewChar = (const char*)fileMapView;
assert(fileMapView != NULL);
#else
FILE* f;
long file_size;
struct stat sb;
char* p;
int fd;
(*len) = 0;
f = fopen(filename, "r");
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fclose(f);
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open");
return NULL;
}
if (fstat(fd, &sb) == -1) {
perror("fstat");
return NULL;
}
if (!S_ISREG(sb.st_mode)) {
fprintf(stderr, "%s is not a file\n", "lineitem.tbl");
return NULL;
}
p = (char*)mmap(0, (size_t)file_size, PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
perror("mmap");
return NULL;
}
if (close(fd) == -1) {
perror("close");
return NULL;
}
(*len) = (size_t)file_size;
return p;
#endif
}
#if 0
static int gz_load(std::vector<char>* buf, const char* filename)
{
#ifdef ENABLE_ZLIB
gzFile file;
file = gzopen (filename, "r");
if (! file) {
fprintf (stderr, "gzopen of '%s' failed: %s.\n", filename,
strerror (errno));
exit (EXIT_FAILURE);
return false;
}
while (1) {
int err;
int bytes_read;
unsigned char buffer[1024];
bytes_read = gzread (file, buffer, 1024);
buf->insert(buf->end(), buffer, buffer + 1024);
//printf ("%s", buffer);
if (bytes_read < 1024) {
if (gzeof (file)) {
break;
}
else {
const char * error_string;
error_string = gzerror (file, & err);
if (err) {
fprintf (stderr, "Error: %s.\n", error_string);
exit (EXIT_FAILURE);
return false;
}
}
}
}
gzclose (file);
return true;
#else
return false;
#endif
}
#endif
static const char* get_file_data(size_t* len, const char* filename) {
const char* ext = strrchr(filename, '.');
size_t data_len = 0;
const char* data = NULL;
if (strcmp(ext, ".gz") == 0) {
assert(0); /* todo */
#if 0
std::vector<char> buf;
bool ret = gz_load(&buf, filename);
if (ret) {
char *p = static_cast<char*>(malloc(buf.size() + 1)); // @fixme { implement deleter }
memcpy(p, &buf.at(0), buf.size());
p[buf.size()] = '\0';
data = p;
data_len = buf.size();
}
#endif
} else {
data = mmap_file(&data_len, filename);
}
(*len) = data_len;
return data;
}
static int LoadObjAndConvert(float bmin[3], float bmax[3],
const char* filename) {
tinyobj_attrib_t attrib;
tinyobj_shape_t* shapes = NULL;
size_t num_shapes;
tinyobj_material_t* materials = NULL;
size_t num_materials;
size_t data_len = 0;
const char* data = get_file_data(&data_len, filename);
if (data == NULL) {
exit(-1);
return 0;
}
printf("filesize: %d\n", (int)data_len);
{
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
int ret = tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials,
&num_materials, data, data_len, flags);
if (ret != TINYOBJ_SUCCESS) {
return 0;
}
printf("# of shapes = %d\n", (int)num_shapes);
printf("# of materiasl = %d\n", (int)num_materials);
{
int i;
for (i = 0; i < num_shapes; i++) {
printf("shape[%d] name = %s\n", i, shapes[i].name);
}
}
}
bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
bmax[0] = bmax[1] = bmax[2] = -FLT_MAX;
{
DrawObject o;
float* vb;
/* std::vector<float> vb; // */
size_t face_offset = 0;
size_t i;
/* Assume triangulated face. */
size_t num_triangles = attrib.num_face_num_verts;
size_t stride = 9; /* 9 = pos(3float), normal(3float), color(3float) */
vb = (float*)malloc(sizeof(float) * stride * num_triangles * 3);
for (i = 0; i < attrib.num_face_num_verts; i++) {
size_t f;
assert(attrib.face_num_verts[i] % 3 ==
0); /* assume all triangle faces. */
for (f = 0; f < attrib.face_num_verts[i] / 3; f++) {
int k;
float v[3][3];
float n[3][3];
float c[3];
float len2;
tinyobj_vertex_index_t idx0 = attrib.faces[face_offset + 3 * f + 0];
tinyobj_vertex_index_t idx1 = attrib.faces[face_offset + 3 * f + 1];
tinyobj_vertex_index_t idx2 = attrib.faces[face_offset + 3 * f + 2];
for (k = 0; k < 3; k++) {
int f0 = idx0.v_idx;
int f1 = idx1.v_idx;
int f2 = idx2.v_idx;
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] = (v[0][k] < bmin[k]) ? v[0][k] : bmin[k];
bmin[k] = (v[1][k] < bmin[k]) ? v[1][k] : bmin[k];
bmin[k] = (v[2][k] < bmin[k]) ? v[2][k] : bmin[k];
bmax[k] = (v[0][k] > bmax[k]) ? v[0][k] : bmax[k];
bmax[k] = (v[1][k] > bmax[k]) ? v[1][k] : bmax[k];
bmax[k] = (v[2][k] > bmax[k]) ? v[2][k] : bmax[k];
}
if (attrib.num_normals > 0) {
int f0 = idx0.vn_idx;
int f1 = idx1.vn_idx;
int f2 = idx2.vn_idx;
if (f0 >= 0 && f1 >= 0 && f2 >= 0) {
assert(3 * f0 + 2 < attrib.num_normals);
assert(3 * f1 + 2 < attrib.num_normals);
assert(3 * f2 + 2 < attrib.num_normals);
for (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 { /* normal index is not defined for this face */
/* compute geometric normal */
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
}
} else {
/* compute geometric normal */
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
}
for (k = 0; k < 3; k++) {
vb[(3 * i + k) * stride + 0] = v[k][0];
vb[(3 * i + k) * stride + 1] = v[k][1];
vb[(3 * i + k) * stride + 2] = v[k][2];
vb[(3 * i + k) * stride + 3] = n[k][0];
vb[(3 * i + k) * stride + 4] = n[k][1];
vb[(3 * i + k) * stride + 5] = n[k][2];
/* Use normal as color. */
c[0] = n[k][0];
c[1] = n[k][1];
c[2] = n[k][2];
len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = (float)sqrt(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb[(3 * i + k) * stride + 6] = (c[0] * 0.5 + 0.5);
vb[(3 * i + k) * stride + 7] = (c[1] * 0.5 + 0.5);
vb[(3 * i + k) * stride + 8] = (c[2] * 0.5 + 0.5);
}
}
face_offset += attrib.face_num_verts[i];
}
o.vb = 0;
o.numTriangles = 0;
if (num_triangles > 0) {
glGenBuffers(1, &o.vb);
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glBufferData(GL_ARRAY_BUFFER, num_triangles * 3 * stride * sizeof(float),
vb, GL_STATIC_DRAW);
o.numTriangles = num_triangles;
}
free(vb);
gDrawObject = o;
}
printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
tinyobj_attrib_free(&attrib);
tinyobj_shapes_free(shapes, num_shapes);
tinyobj_materials_free(materials, num_materials);
return 1;
}
static void reshapeFunc(GLFWwindow* window, int w, int h) {
int fb_w, fb_h;
glfwGetFramebufferSize(window, &fb_w, &fb_h);
glViewport(0, 0, fb_w, fb_h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (GLdouble)w / (GLdouble)h, (GLdouble)0.01f,
(GLdouble)100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
width = w;
height = h;
}
static void keyboardFunc(GLFWwindow* window, int key, int scancode, int action,
int mods) {
(void)window;
(void)scancode;
(void)mods;
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
/* Move camera */
float mv_x = 0, mv_y = 0, mv_z = 0;
if (key == GLFW_KEY_K)
mv_x += 1;
else if (key == GLFW_KEY_J)
mv_x += -1;
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;
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE)
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
static void clickFunc(GLFWwindow* window, int button, int action, int mods) {
(void)window;
(void)mods;
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
mouseLeftPressed = 1;
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
} else if (action == GLFW_RELEASE) {
mouseLeftPressed = 0;
}
}
if (button == GLFW_MOUSE_BUTTON_RIGHT) {
if (action == GLFW_PRESS) {
mouseRightPressed = 1;
} else if (action == GLFW_RELEASE) {
mouseRightPressed = 0;
}
}
if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
if (action == GLFW_PRESS) {
mouseMiddlePressed = 1;
} else if (action == GLFW_RELEASE) {
mouseMiddlePressed = 0;
}
}
}
static void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
float rotScale = 1.0f;
float transScale = 2.0f;
(void)window;
if (mouseLeftPressed) {
trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width,
rotScale * (height - 2.0f * prevMouseY) / (float)height,
rotScale * (2.0f * (float)mouse_x - width) / (float)width,
rotScale * (height - 2.0f * (float)mouse_y) / (float)height);
add_quats(prev_quat, curr_quat, curr_quat);
} else if (mouseMiddlePressed) {
eye[0] -= transScale * ((float)mouse_x - prevMouseX) / (float)width;
lookat[0] -= transScale * ((float)mouse_x - prevMouseX) / (float)width;
eye[1] += transScale * ((float)mouse_y - prevMouseY) / (float)height;
lookat[1] += transScale * ((float)mouse_y - prevMouseY) / (float)height;
} else if (mouseRightPressed) {
eye[2] += transScale * ((float)mouse_y - prevMouseY) / (float)height;
lookat[2] += transScale * ((float)mouse_y - prevMouseY) / (float)height;
}
prevMouseX = (float)mouse_x;
prevMouseY = (float)mouse_y;
}
static void Draw(const DrawObject* draw_object) {
int i;
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
glColor3f(1.0f, 1.0f, 1.0f);
if (draw_object->vb >= 1) {
glBindBuffer(GL_ARRAY_BUFFER, draw_object->vb);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 36, (const void*)0);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float) * 3));
glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float) * 6));
glDrawArrays(GL_TRIANGLES, 0, 3 * draw_object->numTriangles);
CheckErrors("drawarrays");
}
/* draw wireframe */
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);
glColor3f(0.0f, 0.0f, 0.4f);
if (draw_object->vb >= 1) {
glBindBuffer(GL_ARRAY_BUFFER, draw_object->vb);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 36, (const void*)0);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float) * 3));
glDrawArrays(GL_TRIANGLES, 0, 3 * draw_object->numTriangles);
CheckErrors("drawarrays");
}
}
static void Init() {
trackball(curr_quat, 0, 0, 0, 0);
eye[0] = 0.0f;
eye[1] = 0.0f;
eye[2] = 3.0f;
lookat[0] = 0.0f;
lookat[1] = 0.0f;
lookat[2] = 0.0f;
up[0] = 0.0f;
up[1] = 1.0f;
up[2] = 0.0f;
}
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Needs input.obj\n");
return 0;
}
Init();
printf("Initialize GLFW...\n");
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW.\n");
return -1;
}
gWindow = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
if (gWindow == NULL) {
fprintf(stderr, "Failed to open GLFW window.\n");
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(gWindow);
glfwSwapInterval(1);
glfwSetWindowSizeCallback(gWindow, reshapeFunc);
glfwSetKeyCallback(gWindow, keyboardFunc);
glfwSetMouseButtonCallback(gWindow, clickFunc);
glfwSetCursorPosCallback(gWindow, motionFunc);
/* glewExperimental = 1; */
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW.\n");
return -1;
}
reshapeFunc(gWindow, width, height);
{
float bmin[3], bmax[3];
float maxExtent;
if (0 == LoadObjAndConvert(bmin, bmax, argv[1])) {
printf("failed to load & conv\n");
return -1;
}
maxExtent = 0.5f * (bmax[0] - bmin[0]);
if (maxExtent < 0.5f * (bmax[1] - bmin[1])) {
maxExtent = 0.5f * (bmax[1] - bmin[1]);
}
if (maxExtent < 0.5f * (bmax[2] - bmin[2])) {
maxExtent = 0.5f * (bmax[2] - bmin[2]);
}
while (glfwWindowShouldClose(gWindow) == GL_FALSE) {
GLfloat mat[4][4];
glfwPollEvents();
glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt((GLdouble)eye[0], (GLdouble)eye[1], (GLdouble)eye[2],
(GLdouble)lookat[0], (GLdouble)lookat[1], (GLdouble)lookat[2],
(GLdouble)up[0], (GLdouble)up[1], (GLdouble)up[2]);
build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]);
/* Fit to -1, 1 */
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
/* Centerize object. */
glTranslatef(-0.5f * (bmax[0] + bmin[0]), -0.5f * (bmax[1] + bmin[1]),
-0.5f * (bmax[2] + bmin[2]));
Draw(&gDrawObject);
glfwSwapBuffers(gWindow);
}
}
glfwTerminate();
}

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 verbose) bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads)
{ {
#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,15 +229,11 @@ 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;
option.verbose = verbose; bool ret = parseObj(&attrib, &shapes, data, data_len, option);
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)
@@ -245,15 +241,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::index_t idx0 = attrib.indices[face_offset+3*f+0]; tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0];
tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1]; tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1];
tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2]; tinyobj_opt::vertex_index idx2 = attrib.faces[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.vertex_index; int f0 = idx0.v_idx;
int f1 = idx1.vertex_index; int f1 = idx1.v_idx;
int f2 = idx2.vertex_index; int f2 = idx2.v_idx;
assert(f0 >= 0); assert(f0 >= 0);
assert(f1 >= 0); assert(f1 >= 0);
assert(f2 >= 0); assert(f2 >= 0);
@@ -272,24 +268,19 @@ 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 nf0 = idx0.normal_index; int f0 = idx0.vn_idx;
int nf1 = idx1.normal_index; int f1 = idx1.vn_idx;
int nf2 = idx2.normal_index; int f2 = idx2.vn_idx;
assert(f0 >= 0);
if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) { assert(f1 >= 0);
assert(3*nf0+2 < attrib.normals.size()); assert(f2 >= 0);
assert(3*nf1+2 < attrib.normals.size()); assert(3*f0+2 < attrib.normals.size());
assert(3*nf2+2 < attrib.normals.size()); assert(3*f1+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
@@ -308,7 +299,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 > 1.0e-6f) { if (len2 > 0.0f) {
float len = sqrtf(len2); float len = sqrtf(len2);
c[0] /= len; c[0] /= len;
@@ -339,6 +330,9 @@ 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)
@@ -505,31 +499,24 @@ static void Init() {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc < 2) { if (argc < 2) {
std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << std::endl; std::cout << "Needs input.obj\n" << 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 = (atoi(argv[3]) > 0) ? true : false; benchmark_only = true;
}
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]);
@@ -540,9 +527,7 @@ 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;
option.verbose = true; bool ret = parseObj(&attrib, &shapes, data, data_len, option);
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
return ret; return ret;
} }
@@ -584,7 +569,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, verbose)) { if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads)) {
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 <fstream> #include <cassert>
#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 <mmsystem.h>
#include <windows.h> #include <windows.h>
#include <mmsystem.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,8 +58,7 @@ class timerutil {
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 + return this->sec() * 1000000 + static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
} }
time_t current() { time_t current() {
struct timeval t; struct timeval t;
@@ -82,7 +81,7 @@ class timerutil {
#endif #endif
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
DWORD t_[2]; DWORD t_[2];
#else #else
@@ -95,101 +94,94 @@ class timerutil {
#endif #endif
}; };
static void PrintInfo(const tinyobj::attrib_t& attrib, static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials)
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::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl;
<< 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), printf("shape[%ld].name = %s\n", static_cast<long>(i), shapes[i].name.c_str());
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()));
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() == assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.material_ids.size());
shapes[i].mesh.material_ids.size());
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
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), printf(" face[%ld].fnum = %ld\n", static_cast<long>(f), static_cast<unsigned long>(fnum));
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), 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);
static_cast<long>(v), idx.vertex_index, idx.normal_index,
idx.texcoord_index);
} }
printf(" face[%ld].material_id = %d\n", static_cast<long>(f), printf(" face[%ld].material_id = %d\n", static_cast<long>(f), shapes[i].mesh.material_ids[f]);
shapes[i].mesh.material_ids[f]);
index_offset += fnum; index_offset += fnum;
} }
printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i), printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.tags.size()));
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), printf(" tag[%ld] = %s ", static_cast<long>(t), shapes[i].mesh.tags[t].name.c_str());
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>( {
shapes[i].mesh.tags[t].floatValues[j])); printf("%f", static_cast<const double>(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(", ");
} }
} }
@@ -199,59 +191,25 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
} }
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), printf("material[%ld].name = %s\n", static_cast<long>(i), materials[i].name.c_str());
materials[i].name.c_str()); 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]));
printf(" material.Ka = (%f, %f ,%f)\n", 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]));
static_cast<const double>(materials[i].ambient[0]), 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[1]), 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[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.Kd = (%f, %f ,%f)\n", printf(" material.Ns = %f\n", static_cast<const double>(materials[i].shininess));
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", printf(" material.dissolve = %f\n", static_cast<const double>(materials[i].dissolve));
static_cast<const double>(materials[i].dissolve));
printf(" material.illum = %d\n", materials[i].illum); 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", printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str());
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());
printf(" <<PBR>>\n"); std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
printf(" material.Pr = %f\n", materials[i].roughness); std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
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());
@@ -260,8 +218,12 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
} }
} }
static bool TestLoadObj(const char* filename, const char* basepath = NULL, static bool
bool triangulate = true) { TestLoadObj(
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;
@@ -271,8 +233,7 @@ static bool TestLoadObj(const char* filename, const char* basepath = NULL,
timerutil t; timerutil t;
t.start(); t.start();
std::string err; std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate);
basepath, triangulate);
t.end(); t.end();
printf("Parsing time: %lu [msecs]\n", t.msec()); printf("Parsing time: %lu [msecs]\n", t.msec());
@@ -290,11 +251,15 @@ static bool TestLoadObj(const char* filename, const char* basepath = NULL,
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 << "mtllib cube.mtl\n" objStream
<< "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"
@@ -326,7 +291,7 @@ static bool TestStreamLoadObj() {
"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"
@@ -353,15 +318,18 @@ static bool TestStreamLoadObj() {
"Ks 0 0 0"); "Ks 0 0 0");
using namespace tinyobj; using namespace tinyobj;
class MaterialStringStreamReader : public MaterialReader { class MaterialStringStreamReader:
public MaterialReader
{
public: public:
MaterialStringStreamReader(const std::string& matSStream) MaterialStringStreamReader(const std::string& matSStream): m_matSStream(matSStream) {}
: m_matSStream(matSStream) {}
virtual ~MaterialStringStreamReader() {} virtual ~MaterialStringStreamReader() {}
virtual bool operator()(const std::string& matId, virtual bool operator() (
const std::string& matId,
std::vector<material_t>* materials, std::vector<material_t>* materials,
std::map<std::string, int>* matMap, std::map<std::string, int>* matMap,
std::string* err) { std::string* err)
{
(void)matId; (void)matId;
(void)err; (void)err;
LoadMtl(matMap, materials, &m_matSStream); LoadMtl(matMap, materials, &m_matSStream);
@@ -377,8 +345,7 @@ static bool TestStreamLoadObj() {
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, bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader);
&matSSReader);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
@@ -393,7 +360,11 @@ static bool TestStreamLoadObj() {
return true; return true;
} }
int main(int argc, char** argv) { int
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) {
@@ -401,11 +372,10 @@ int main(int argc, char** argv) {
} }
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 == assert(true == TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
} }
return 0; return 0;

View File

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

View File

@@ -1,7 +0,0 @@
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

View File

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

View File

@@ -1,7 +0,0 @@
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

View File

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

View File

@@ -1,7 +0,0 @@
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

View File

@@ -1,19 +0,0 @@
# .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

View File

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

View File

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

View File

@@ -1,170 +1,127 @@
// 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* pyTupleFromfloat3(float array[3]) { PyObject*
pyTupleFromfloat3 (float array[3])
{
int i; int i;
PyObject* tuple = PyTuple_New(3); 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* pyLoadObj(PyObject* self, PyObject* args) { static PyObject*
PyObject *rtndict, *pyshapes, *pymaterials, *attribobj, *current, *meshobj; pyLoadObj(PyObject* self, PyObject* args)
{
PyObject *rtndict, *pyshapes, *pymaterials,
*current, *meshobj;
char const* current_name;
char const* filename; char const* filename;
char *current_name;
vectd vect; vectd vect;
std::vector<tinyobj::index_t> indices;
std::vector<unsigned char> face_verts;
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL; if(!PyArg_ParseTuple(args, "s", &filename))
return NULL;
std::string err; std::string err;
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename); tinyobj::LoadObj(shapes, materials, err, filename);
pyshapes = PyDict_New(); pyshapes = PyDict_New();
pymaterials = PyDict_New(); pymaterials = PyDict_New();
rtndict = PyDict_New(); rtndict = PyDict_New();
attribobj = PyDict_New(); for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin() ;
shape != shapes.end(); shape++)
for (int i = 0; i <= 2; i++) { {
current = PyList_New(0);
switch (i) {
case 0:
current_name = "vertices";
vect = vectd(attrib.vertices.begin(), attrib.vertices.end());
break;
case 1:
current_name = "normals";
vect = vectd(attrib.normals.begin(), attrib.normals.end());
break;
case 2:
current_name = "texcoords";
vect = vectd(attrib.texcoords.begin(), attrib.texcoords.end());
break;
}
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
}
PyDict_SetItemString(attribobj, current_name, current);
}
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
shape != shapes.end(); shape++) {
meshobj = PyDict_New(); meshobj = PyDict_New();
tinyobj::mesh_t cm = (*shape).mesh; tinyobj::mesh_t cm = (*shape).mesh;
for (int i = 0; i <= 4; i++ )
{ {
current = PyList_New(0); current = PyList_New(0);
for (size_t i = 0; i < cm.indices.size(); i++) { switch(i) {
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
// ... case 0:
PyList_Insert(current, 3 * i + 0, current_name = "positions";
PyLong_FromLong(cm.indices[i].vertex_index)); vect = vectd(cm.positions.begin(), cm.positions.end()); break;
PyList_Insert(current, 3 * i + 1, case 1:
PyLong_FromLong(cm.indices[i].normal_index)); current_name = "normals";
PyList_Insert(current, 3 * i + 2, vect = vectd(cm.normals.begin(), cm.normals.end()); break;
PyLong_FromLong(cm.indices[i].texcoord_index)); case 2:
} current_name = "texcoords";
vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); break;
PyDict_SetItemString(meshobj, "indices", current); case 3:
current_name = "indicies";
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++)
{ {
current = PyList_New(0); PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
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); PyDict_SetItemString(meshobj, current_name, 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); PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
} }
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin(); for (std::vector<tinyobj::material_t>::iterator mat = materials.begin() ;
mat != materials.end(); mat++) { mat != materials.end(); mat++)
PyObject* matobj = PyDict_New(); {
PyObject* unknown_parameter = PyDict_New(); PyObject *matobj = PyDict_New();
PyObject *unknown_parameter = PyDict_New();
for (std::map<std::string, std::string>::iterator p = for (std::map<std::string, std::string>::iterator p = (*mat).unknown_parameter.begin() ;
(*mat).unknown_parameter.begin(); p != (*mat).unknown_parameter.end(); ++p)
p != (*mat).unknown_parameter.end(); ++p) { {
PyDict_SetItemString(unknown_parameter, p->first.c_str(), PyDict_SetItemString(unknown_parameter, p->first.c_str(), PyUnicode_FromString(p->second.c_str()));
PyUnicode_FromString(p->second.c_str()));
} }
PyDict_SetItemString(matobj, "shininess", PyDict_SetItemString(matobj, "shininess", PyFloat_FromDouble((*mat).shininess));
PyFloat_FromDouble((*mat).shininess));
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior)); PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
PyDict_SetItemString(matobj, "dissolve", PyDict_SetItemString(matobj, "dissolve", PyFloat_FromDouble((*mat).dissolve));
PyFloat_FromDouble((*mat).dissolve));
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum)); PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
PyDict_SetItemString(matobj, "ambient_texname", PyDict_SetItemString(matobj, "ambient_texname", PyUnicode_FromString((*mat).ambient_texname.c_str()));
PyUnicode_FromString((*mat).ambient_texname.c_str())); PyDict_SetItemString(matobj, "diffuse_texname", PyUnicode_FromString((*mat).diffuse_texname.c_str()));
PyDict_SetItemString(matobj, "diffuse_texname", PyDict_SetItemString(matobj, "specular_texname", PyUnicode_FromString((*mat).specular_texname.c_str()));
PyUnicode_FromString((*mat).diffuse_texname.c_str())); PyDict_SetItemString(matobj, "specular_highlight_texname", PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
PyDict_SetItemString(matobj, "specular_texname", PyDict_SetItemString(matobj, "bump_texname", PyUnicode_FromString((*mat).bump_texname.c_str()));
PyUnicode_FromString((*mat).specular_texname.c_str())); PyDict_SetItemString(matobj, "displacement_texname", PyUnicode_FromString((*mat).displacement_texname.c_str()));
PyDict_SetItemString( PyDict_SetItemString(matobj, "alpha_texname", PyUnicode_FromString((*mat).alpha_texname.c_str()));
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, "ambient", pyTupleFromfloat3((*mat).ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse)); PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
PyDict_SetItemString(matobj, "specular", PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3((*mat).specular));
pyTupleFromfloat3((*mat).specular)); PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3((*mat).transmittance));
PyDict_SetItemString(matobj, "transmittance", PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3((*mat).emission));
pyTupleFromfloat3((*mat).transmittance));
PyDict_SetItemString(matobj, "emission",
pyTupleFromfloat3((*mat).emission));
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter); PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj); PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
@@ -176,16 +133,28 @@ static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
return rtndict; return rtndict;
} }
static PyMethodDef mMethods[] = { static PyMethodDef mMethods[] = {
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL} {"LoadObj", pyLoadObj, METH_VARARGS},
{NULL, NULL, 0, NULL}
}; };
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
NULL, -1, mMethods};
PyMODINIT_FUNC PyInit_tinyobjloader(void) { static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"tinyobjloader",
NULL,
-1,
mMethods
};
PyMODINIT_FUNC
PyInit_tinyobjloader(void)
{
return PyModule_Create(&moduledef); 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,107 +319,10 @@ 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,6 +24,33 @@ 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
// //
// //
@@ -35,9 +62,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 {
@@ -64,22 +91,6 @@ 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;
@@ -101,9 +112,8 @@ typedef struct {
typedef struct { typedef struct {
std::vector<index_t> indices; std::vector<index_t> indices;
std::vector<unsigned char> num_face_vertices; // The number of vertices per std::vector<unsigned char>
// face. 3 = polygon, 4 = quad, num_face_vertices; // The number of vertices per face. 3 = polygon, 4 = quad, ... Up to 255.
// ... Up to 255.
std::vector<int> material_ids; // per-face material ID std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag std::vector<tag_t> tags; // SubD tag
} mesh_t; } mesh_t;
@@ -121,22 +131,14 @@ typedef struct {
} attrib_t; } attrib_t;
typedef struct callback_t_ { typedef struct callback_t_ {
// 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);
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);
// y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in // -2147483648 will be passed for undefined index
// `vt` line. void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx);
void (*texcoord_cb)(void *user_data, float x, float y, float z); // `name` material name, `materialId` = the array index of material_t[]. -1 if
// 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 material_id); void (*usemtl_cb)(void *user_data, const char *name, int materialId);
// `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);
@@ -153,6 +155,7 @@ 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 {
@@ -197,11 +200,12 @@ 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`
/// See `examples/callback_api/` for how to use this function. /// 'mtl_basepath' is optional, and used for base path for .mtl file.
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, /// 'triangulate' is optional, and used whether triangulate polygon face in .obj
void *user_data = NULL, /// or not.
MaterialReader *readMatFn = NULL, bool LoadObjWithCallback(void *user_data, const callback_t &callback,
std::string *err = NULL); std::string *err, std::istream *inStream,
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.
@@ -219,12 +223,12 @@ void LoadMtl(std::map<std::string, int> *material_map,
} // namespace tinyobj } // namespace tinyobj
#ifdef TINYOBJLOADER_IMPLEMENTATION #ifdef TINYOBJLOADER_IMPLEMENTATION
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cctype>
#include <utility> #include <utility>
#include <fstream> #include <fstream>
@@ -257,38 +261,6 @@ 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))
@@ -452,13 +424,18 @@ fail:
return false; return false;
} }
static inline float parseFloat(const char **token, double default_value = 0.0) { static inline float parseFloat(const char **token) {
(*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 = default_value; double val = 0.0;
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;
} }
@@ -474,14 +451,6 @@ 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;
@@ -541,7 +510,8 @@ 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(static_cast<int>(0)); // 0 is an invalid index in OBJ vertex_index vi(
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");
@@ -592,20 +562,6 @@ 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();
} }
@@ -660,8 +616,7 @@ static bool exportFaceGroupToShape(
shape->mesh.indices.push_back(idx); shape->mesh.indices.push_back(idx);
} }
shape->mesh.num_face_vertices.push_back( shape->mesh.num_face_vertices.push_back(static_cast<unsigned char>(npolys));
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
} }
} }
@@ -678,15 +633,12 @@ 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) {
std::string linebuf; inStream->getline(&buf[0], static_cast<std::streamsize>(maxchars));
safeGetline(*inStream, linebuf); 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) {
@@ -770,8 +722,7 @@ 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);
@@ -826,55 +777,6 @@ 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;
@@ -931,41 +833,6 @@ 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) {
@@ -1059,9 +926,12 @@ 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) {
std::string linebuf; inStream->getline(&buf[0], maxchars);
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) {
@@ -1323,26 +1193,21 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
return true; return true;
} }
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, bool LoadObjWithCallback(void *user_data, const callback_t &callback,
void *user_data /*= NULL*/, std::string *err, std::istream *inStream,
MaterialReader *readMatFn /*= NULL*/, MaterialReader *readMatFn) {
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 material_id = -1; // -1 = invalid int materialId = -1; // -1 = invalid
std::vector<index_t> indices; int maxchars = 8192; // Alloc enough size.
std::vector<material_t> materials; std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
std::vector<std::string> names; while (inStream->peek() != -1) {
names.reserve(2); inStream->getline(&buf[0], maxchars);
std::string name;
std::vector<const char *> names_out;
std::string linebuf; std::string linebuf(&buf[0]);
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) {
@@ -1371,10 +1236,10 @@ bool LoadObjWithCallback(std::istream &inStream, 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, w; // w is optional. default = 1.0 float x, y, z;
parseV(&x, &y, &z, &w, &token); parseFloat3(&x, &y, &z, &token);
if (callback.vertex_cb) { if (callback.vertex_cb) {
callback.vertex_cb(user_data, x, y, z, w); callback.vertex_cb(user_data, x, y, z);
} }
continue; continue;
} }
@@ -1393,10 +1258,10 @@ bool LoadObjWithCallback(std::istream &inStream, 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, z; // y and z are optional. default = 0.0 float x, y;
parseFloat3(&x, &y, &z, &token); parseFloat2(&x, &y, &token);
if (callback.texcoord_cb) { if (callback.texcoord_cb) {
callback.texcoord_cb(user_data, x, y, z); callback.texcoord_cb(user_data, x, y);
} }
continue; continue;
} }
@@ -1406,25 +1271,15 @@ bool LoadObjWithCallback(std::istream &inStream, 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) {
index_t idx; callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_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;
} }
@@ -1433,8 +1288,7 @@ bool LoadObjWithCallback(std::istream &inStream, 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, sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
static_cast<unsigned int>(_countof(namebuf)));
#else #else
sscanf(token, "%s", namebuf); sscanf(token, "%s", namebuf);
#endif #endif
@@ -1446,12 +1300,12 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// { error!! material not found } // { error!! material not found }
} }
if (newMaterialId != material_id) { if (newMaterialId != materialId) {
material_id = newMaterialId; materialId = newMaterialId;
} }
if (callback.usemtl_cb) { if (callback.usemtl_cb) {
callback.usemtl_cb(user_data, namebuf, material_id); callback.usemtl_cb(user_data, namebuf, materialId);
} }
continue; continue;
@@ -1459,7 +1313,6 @@ bool LoadObjWithCallback(std::istream &inStream, 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]))) {
if (readMatFn) {
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7; token += 7;
#ifdef _MSC_VER #ifdef _MSC_VER
@@ -1469,7 +1322,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
#endif #endif
std::string err_mtl; std::string err_mtl;
materials.clear(); std::vector<material_t> materials;
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;
@@ -1483,14 +1336,14 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
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;
} }
// group name // group name
if (token[0] == 'g' && IS_SPACE((token[1]))) { if (token[0] == 'g' && IS_SPACE((token[1]))) {
names.clear(); std::vector<std::string> names;
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);
@@ -1500,26 +1353,28 @@ bool LoadObjWithCallback(std::istream &inStream, 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.clear(); name = "";
} }
if (callback.group_cb) { if (callback.group_cb) {
if (names.size() > 1) { if (names.size() > 1) {
// create const char* array. // create const char* array.
names_out.resize(names.size() - 1); std::vector<const char *> tmp(names.size() - 1);
for (size_t j = 0; j < names_out.size(); j++) { for (size_t j = 0; j < tmp.size(); j++) {
names_out[j] = names[j + 1].c_str(); tmp[j] = names[j + 1].c_str();
} }
callback.group_cb(user_data, &names_out.at(0), callback.group_cb(user_data, &tmp.at(0),
static_cast<int>(names_out.size())); static_cast<int>(tmp.size()));
} else { } else {
callback.group_cb(user_data, NULL, 0); callback.group_cb(user_data, NULL, 0);
} }
} }
continue; continue;
@@ -1535,10 +1390,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
#else #else
sscanf(token, "%s", namebuf); sscanf(token, "%s", namebuf);
#endif #endif
std::string object_name = std::string(namebuf); std::string name = std::string(namebuf);
if (callback.object_cb) { if (callback.object_cb) {
callback.object_cb(user_data, object_name.c_str()); callback.object_cb(user_data, name.c_str());
} }
continue; continue;