6 Commits

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)
@@ -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 ;-)
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)
tinyobjloader can successfully load 6M triangles Rungholt scene.
@@ -42,20 +48,12 @@ http://graphics.cs.williams.edu/data/meshes.xml
![](images/sanmugel.png)
* [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 ...
### 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
* pbrt-v2 https://github.com/mmp/pbrt-v2
* 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
* cocos2d-x https://github.com/cocos2d/cocos2d-x/
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan
* voxelizer https://github.com/karimnaaji/voxelizer
* Probulator https://github.com/kayru/Probulator
* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking
* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK
* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg
* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl
* Your project here!
## Features
Features
--------
* Group(parse multiple group name)
* Vertex
@@ -86,16 +80,20 @@ TinyObjLoader is successfully used in ...
* Callback API for custom loading.
## TODO
TODO
----
* [ ] Fix Python binding.
* [ ] Fix obj_sticker example.
* [ ] More unit test codes.
## License
License
-------
Licensed under MIT license.
## Usage
Usage
-----
`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`.
@@ -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.
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)
## Tests
Tests
-----
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
@@ -7,6 +7,7 @@ install:
# All external dependencies are installed in C:\projects\deps
#######################################################################################
- mkdir C:\projects\deps
- cd C:\projects\deps
#######################################################################################
# Install Ninja

View File

@@ -1,44 +1,39 @@
//
// 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
#include "tiny_obj_loader.h"
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <cassert>
#include <iostream>
#include <sstream>
#include <fstream>
typedef struct {
typedef struct
{
std::vector<float> vertices;
std::vector<float> normals;
std::vector<float> texcoords;
std::vector<int> v_indices;
std::vector<int> vn_indices;
std::vector<int> vt_indices;
std::vector<int> v_indices;
std::vector<int> vn_indices;
std::vector<int> vt_indices;
std::vector<tinyobj::material_t> materials;
} MyMesh;
void vertex_cb(void *user_data, float x, float y, float z, float w) {
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
printf("v[%ld] = %f, %f, %f (w %f)\n", mesh->vertices.size() / 3, x, y, z, 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\n", mesh->vertices.size() / 3, x, y, z);
mesh->vertices.push_back(x);
mesh->vertices.push_back(y);
mesh->vertices.push_back(z);
// Discard w
}
void normal_cb(void *user_data, float x, float y, float z) {
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
void normal_cb(void *user_data, float x, float y, float z)
{
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z);
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);
}
void texcoord_cb(void *user_data, float x, float y, float z) {
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
printf("vt[%ld] = %f, %f, %f\n", mesh->texcoords.size() / 3, x, y, z);
void texcoord_cb(void *user_data, float x, float y)
{
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(y);
mesh->texcoords.push_back(z);
}
void index_cb(void *user_data, tinyobj::index_t *indices, int num_indices) {
// NOTE: the value of each index is raw value.
void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx)
{
// NOTE: the value of each index is raw value.
// For example, the application must manually adjust the index with offset
// (e.g. v_indices.size()) when the value is negative(whic means relative
// index).
// Also, the first index starts with 1, not 0.
// (e.g. v_indices.size()) when the value is negative(relative index).
// See fixIndex() function in tiny_obj_loader.h for details.
// Also, 0 is set for the index value which
// does not exist in .obj
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
// Also, -2147483648(0x80000000) is set for the index value which does not exist in .obj
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++) {
tinyobj::index_t idx = indices[i];
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) {
mesh->vn_indices.push_back(idx.normal_index);
}
if (idx.texcoord_index != 0) {
mesh->vt_indices.push_back(idx.texcoord_index);
}
if (v_idx != 0x80000000) {
mesh->v_indices.push_back(v_idx);
}
if (vn_idx != 0x80000000) {
mesh->vn_indices.push_back(vn_idx);
}
if (vt_idx != 0x80000000) {
mesh->vt_indices.push_back(vt_idx);
}
}
void usemtl_cb(void *user_data, const char *name, int material_idx) {
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
void usemtl_cb(void *user_data, const char* name, int material_idx)
{
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
if ((material_idx > -1) && (material_idx < mesh->materials.size())) {
printf("usemtl. material id = %d(name = %s)\n", material_idx,
mesh->materials[material_idx].name.c_str());
printf("usemtl. material id = %d(name = %s)\n", material_idx, mesh->materials[material_idx].name.c_str());
} else {
printf("usemtl. name = %s\n", name);
}
}
void mtllib_cb(void *user_data, const tinyobj::material_t *materials,
int num_materials) {
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_materials)
{
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("mtllib. # of materials = %d\n", num_materials);
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) {
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
void group_cb(void *user_data, const char **names, int num_names)
{
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("group : name = \n");
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) {
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
void object_cb(void *user_data, const char *name)
{
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("object : name = %s\n", name);
}
int main(int argc, char **argv) {
int
main(int argc, char** argv)
{
tinyobj::callback_t cb;
cb.vertex_cb = vertex_cb;
cb.normal_cb = normal_cb;
@@ -130,11 +123,7 @@ int main(int argc, char **argv) {
MyMesh mesh;
std::string err;
std::string filename = "../../models/cornell_box.obj";
if (argc > 1) {
filename = std::string(argv[1]);
}
std::ifstream ifs(filename.c_str());
std::ifstream ifs("../../models/cornell_box.obj");
if (ifs.fail()) {
std::cerr << "file not found." << std::endl;
@@ -142,8 +131,8 @@ int main(int argc, char **argv) {
}
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()) {
std::cerr << err << std::endl;

View File

@@ -1,15 +1,15 @@
//
// Simple .obj viewer(vertex only)
//
#include <algorithm>
#include <cassert>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <string>
#include <vector>
#include <cmath>
#include <cassert>
#include <algorithm>
#include <GL/glew.h>
@@ -30,8 +30,8 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <mmsystem.h>
#include <windows.h>
#include <mmsystem.h>
#ifdef __cplusplus
}
#endif
@@ -45,7 +45,7 @@ extern "C" {
#endif
class timerutil {
public:
public:
#ifdef _WIN32
typedef DWORD time_t;
@@ -81,7 +81,7 @@ class timerutil {
return (time_t)(t.tv_sec * 1000 + t.tv_usec);
}
#else // C timer
#else // C timer
// using namespace std;
typedef clock_t time_t;
@@ -96,7 +96,7 @@ class timerutil {
#endif
#endif
private:
private:
#ifdef _WIN32
DWORD t_[2];
#else
@@ -110,7 +110,7 @@ class timerutil {
};
typedef struct {
GLuint vb; // vertex buffer
GLuint vb; // vertex buffer
int numTriangles;
} DrawObject;
@@ -155,15 +155,14 @@ void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
N[0] /= len;
N[1] /= len;
}
}
bool LoadObjAndConvert(float bmin[3], float bmax[3],
std::vector<DrawObject>* drawObjects,
const char* filename) {
bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& drawObjects, const char* filename)
{
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
@@ -171,22 +170,21 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
timerutil tm;
tm.start();
std::string err;
bool ret =
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
if (!err.empty()) {
std::cerr << err << std::endl;
}
tm.end();
if (!ret) {
std::cerr << "Failed to load " << filename << std::endl;
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 normals = %d\n", (int)(attrib.normals.size()) / 3);
@@ -198,105 +196,102 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3],
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
{
for (size_t s = 0; s < shapes.size(); s++) {
DrawObject o;
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) {
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2];
for (size_t s = 0; s < shapes.size(); s++) {
DrawObject o;
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
for (size_t f = 0; f < shapes[s].mesh.indices.size()/3; f++) {
float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
tinyobj::index_t idx0 = shapes[s].mesh.indices[3*f+0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3*f+1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3*f+2];
v[0][k] = attrib.vertices[3 * f0 + k];
v[1][k] = attrib.vertices[3 * f1 + k];
v[2][k] = attrib.vertices[3 * f2 + k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
float n[3][3];
if (attrib.normals.size() > 0) {
int f0 = idx0.normal_index;
int f1 = idx1.normal_index;
int f2 = idx2.normal_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
float v[3][3];
for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3 * f0 + k];
n[1][k] = attrib.normals[3 * f1 + k];
n[2][k] = attrib.normals[3 * f2 + k];
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3*f0+k];
v[1][k] = attrib.vertices[3*f1+k];
v[2][k] = attrib.vertices[3*f2+k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
} 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];
float n[3][3];
if (attrib.normals.size() > 0) {
int f0 = idx0.normal_index;
int f1 = idx1.normal_index;
int f2 = idx2.normal_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3*f0+k];
n[1][k] = attrib.normals[3*f1+k];
n[2][k] = attrib.normals[3*f2+k];
}
} else {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2];
n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2];
}
for (int k = 0; k < 3; k++) {
vb.push_back(v[k][0]);
vb.push_back(v[k][1]);
vb.push_back(v[k][2]);
vb.push_back(n[k][0]);
vb.push_back(n[k][1]);
vb.push_back(n[k][2]);
// Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb.push_back(c[0] * 0.5 + 0.5);
vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5);
}
}
for (int k = 0; k < 3; k++) {
vb.push_back(v[k][0]);
vb.push_back(v[k][1]);
vb.push_back(v[k][2]);
vb.push_back(n[k][0]);
vb.push_back(n[k][1]);
vb.push_back(n[k][2]);
// Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb.push_back(c[0] * 0.5 + 0.5);
vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5);
o.vb = 0;
o.numTriangles = 0;
if (vb.size() > 0) {
glGenBuffers(1, &o.vb);
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW);
o.numTriangles = vb.size() / 9 / 3;
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles);
}
}
o.vb = 0;
o.numTriangles = 0;
if (vb.size() > 0) {
glGenBuffers(1, &o.vb);
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0),
GL_STATIC_DRAW);
o.numTriangles = vb.size() / 9 / 3;
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
}
drawObjects->push_back(o);
gDrawObjects.push_back(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]);
return true;
}
void reshapeFunc(GLFWwindow* window, int w, int h) {
void reshapeFunc(GLFWwindow* window, int w, int h)
{
int fb_w, fb_h;
// Get actual framebuffer size.
glfwGetFramebufferSize(window, &fb_w, &fb_h);
@@ -312,90 +307,78 @@ void reshapeFunc(GLFWwindow* window, int w, int h) {
height = h;
}
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;
// camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
// Close window
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE)
glfwSetWindowShouldClose(window, GL_TRUE);
void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int 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;
//camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
// Close window
if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE);
// init_frame = true;
}
//init_frame = true;
}
}
void clickFunc(GLFWwindow* window, int button, int action, int mods) {
(void)window;
(void)mods;
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
mouseLeftPressed = true;
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
} else if (action == GLFW_RELEASE) {
mouseLeftPressed = false;
void clickFunc(GLFWwindow* window, int button, int action, int mods){
if(button == GLFW_MOUSE_BUTTON_LEFT){
if(action == GLFW_PRESS){
mouseLeftPressed = true;
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
} else if(action == GLFW_RELEASE){
mouseLeftPressed = false;
}
}
}
if (button == GLFW_MOUSE_BUTTON_RIGHT) {
if (action == GLFW_PRESS) {
mouseRightPressed = true;
} else if (action == GLFW_RELEASE) {
mouseRightPressed = false;
if(button == GLFW_MOUSE_BUTTON_RIGHT){
if(action == GLFW_PRESS){
mouseRightPressed = true;
} else if(action == GLFW_RELEASE){
mouseRightPressed = false;
}
}
}
if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
if (action == GLFW_PRESS) {
mouseMiddlePressed = true;
} else if (action == GLFW_RELEASE) {
mouseMiddlePressed = false;
if(button == GLFW_MOUSE_BUTTON_MIDDLE){
if(action == GLFW_PRESS){
mouseMiddlePressed = true;
} else if(action == GLFW_RELEASE){
mouseMiddlePressed = false;
}
}
}
}
void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
(void)window;
void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){
float rotScale = 1.0f;
float transScale = 2.0f;
if (mouseLeftPressed) {
trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width,
rotScale * (height - 2.0f * prevMouseY) / (float)height,
rotScale * (2.0f * mouse_x - width) / (float)width,
rotScale * (height - 2.0f * mouse_y) / (float)height);
if(mouseLeftPressed){
trackball(prev_quat,
rotScale * (2.0f * prevMouseX - width) / (float)width,
rotScale * (height - 2.0f * prevMouseY) / (float)height,
rotScale * (2.0f * mouse_x - width) / (float)width,
rotScale * (height - 2.0f * mouse_y) / (float)height);
add_quats(prev_quat, curr_quat, curr_quat);
} else if (mouseMiddlePressed) {
eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
eye[1] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height;
} else if (mouseRightPressed) {
eye[2] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height;
}
add_quats(prev_quat, curr_quat, curr_quat);
} else if (mouseMiddlePressed) {
eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
eye[1] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height;
} else if (mouseRightPressed) {
eye[2] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height;
}
// Update mouse point
prevMouseX = mouse_x;
prevMouseY = mouse_y;
// Update mouse point
prevMouseX = mouse_x;
prevMouseY = mouse_y;
}
void Draw(const std::vector<DrawObject>& drawObjects) {
void Draw(const std::vector<DrawObject>& drawObjects)
{
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
@@ -407,14 +390,14 @@ void Draw(const std::vector<DrawObject>& drawObjects) {
if (o.vb < 1) {
continue;
}
glBindBuffer(GL_ARRAY_BUFFER, o.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));
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3));
glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays");
@@ -431,13 +414,13 @@ void Draw(const std::vector<DrawObject>& drawObjects) {
if (o.vb < 1) {
continue;
}
glBindBuffer(GL_ARRAY_BUFFER, o.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));
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays");
@@ -460,21 +443,26 @@ static void Init() {
up[2] = 0.0f;
}
int main(int argc, char** argv) {
int main(int argc, char **argv)
{
if (argc < 2) {
std::cout << "Needs input.obj\n" << std::endl;
return 0;
}
Init();
if (!glfwInit()) {
if(!glfwInit()){
std::cerr << "Failed to initialize GLFW." << std::endl;
return -1;
}
window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
if (window == NULL) {
if(window == NULL){
std::cerr << "Failed to open GLFW window. " << std::endl;
glfwTerminate();
return 1;
@@ -498,7 +486,7 @@ int main(int argc, char** argv) {
reshapeFunc(window, width, height);
float bmin[3], bmax[3];
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, argv[1])) {
if (false == LoadObjAndConvert(bmin, bmax, gDrawObjects, argv[1])) {
return -1;
}
@@ -510,7 +498,7 @@ int main(int argc, char** argv) {
maxExtent = 0.5f * (bmax[2] - bmin[2]);
}
while (glfwWindowShouldClose(window) == GL_FALSE) {
while(glfwWindowShouldClose(window) == GL_FALSE) {
glfwPollEvents();
glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -521,8 +509,7 @@ int main(int argc, char** argv) {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat mat[4][4];
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
up[1], up[2]);
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]);
build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]);
@@ -530,9 +517,8 @@ int main(int argc, char** argv) {
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
// Centerize object.
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
-0.5 * (bmax[2] + bmin[2]));
glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2]));
Draw(gDrawObjects);
glfwSwapBuffers(window);

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 displacement_texname; // disp
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;
} material_t;
@@ -317,20 +301,19 @@ typedef struct {
unsigned int length;
} shape_t;
struct index_t {
int vertex_index, texcoord_index, normal_index;
index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {}
explicit index_t(int idx)
: vertex_index(idx), texcoord_index(idx), normal_index(idx) {}
index_t(int vidx, int vtidx, int vnidx)
: vertex_index(vidx), texcoord_index(vtidx), normal_index(vnidx) {}
struct vertex_index {
int v_idx, vt_idx, vn_idx;
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
vertex_index(int vidx, int vtidx, int vnidx)
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
};
typedef struct {
std::vector<float, lt::allocator<float> > vertices;
std::vector<float, lt::allocator<float> > normals;
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> > material_ids;
} 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
static index_t parseRawTriple(const char **token) {
index_t vi(
static vertex_index parseRawTriple(const char **token) {
vertex_index vi(
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] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
@@ -420,7 +404,8 @@ static index_t parseRawTriple(const char **token) {
// i//k
if ((*token)[0] == '/') {
(*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] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
@@ -429,7 +414,8 @@ static index_t parseRawTriple(const char **token) {
}
// 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] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
@@ -440,7 +426,8 @@ static index_t parseRawTriple(const char **token) {
// i/j/k
(*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] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++;
@@ -449,17 +436,17 @@ static index_t parseRawTriple(const char **token) {
}
static inline bool parseString(ShortString *s, const char **token) {
skip_space(token);
size_t e = until_space((*token));
skip_space(token); //(*token) += strspn((*token), " \t");
size_t e = until_space((*token)); // strcspn((*token), " \t\r");
(*s)->insert((*s)->end(), (*token), (*token) + e);
(*token) += e;
return true;
}
static inline int parseInt(const char **token) {
skip_space(token);
skip_space(token); //(*token) += strspn((*token), " \t");
int i = my_atoi((*token));
(*token) += until_space((*token));
(*token) += until_space((*token)); // strcspn((*token), " \t\r");
return i;
}
@@ -598,9 +585,9 @@ assemble :
{
// = pow(5.0, exponent);
double a = 1.0;
double a = 5.0;
for (int i = 0; i < exponent; i++) {
a = a * 5.0;
a = a * a;
}
*result =
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
@@ -614,12 +601,13 @@ fail:
}
static inline float parseFloat(const char **token) {
skip_space(token);
skip_space(token); //(*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) + until_space((*token));
const char *end =
(*token) + until_space((*token)); // strcspn((*token), " \t\r");
double val = 0.0;
tryParseDouble((*token), end, &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]);
// Trim trailing whitespace.
if (linebuf.size() > 0) {
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
}
// Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\n')
@@ -764,8 +747,7 @@ static void LoadMtl(std::map<std::string, int> *material_map,
}
// transmittance
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 2;
float r, g, b;
parseFloat3(&r, &g, &b, &token);
@@ -813,7 +795,6 @@ static void LoadMtl(std::map<std::string, int> *material_map,
material.dissolve = parseFloat(&token);
continue;
}
if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2;
// 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;
}
// 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
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7;
@@ -926,41 +858,6 @@ static void LoadMtl(std::map<std::string, int> *material_map,
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
const char *_space = strchr(token, ' ');
if (!_space) {
@@ -999,8 +896,8 @@ typedef struct {
float tx, ty;
// for f
std::vector<index_t, lt::allocator<index_t> > f;
// std::vector<index_t> f;
std::vector<vertex_index, lt::allocator<vertex_index> > f;
// std::vector<vertex_index> f;
std::vector<int, lt::allocator<int> > f_num_verts;
const char *group_name;
@@ -1021,42 +918,44 @@ struct CommandCount {
size_t num_vn;
size_t num_vt;
size_t num_f;
size_t num_indices;
size_t num_faces;
CommandCount() {
num_v = 0;
num_vn = 0;
num_vt = 0;
num_f = 0;
num_indices = 0;
num_faces = 0;
}
};
class LoadOption {
class
LoadOption
{
public:
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
int req_num_threads;
bool triangulate;
bool verbose;
int req_num_threads;
bool triangulate;
bool verbose;
};
/// Parse wavefront .obj(.obj string data is expanded to linear char array
/// `buf')
/// -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,
std::vector<material_t> *materials, const char *buf, size_t len,
const LoadOption &option);
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
size_t len, const LoadOption& option);
#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
static bool parseLine(Command *command, const char *p, size_t p_len,
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];
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';
const char *token = linebuf;
@@ -1064,7 +963,8 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
command->type = COMMAND_EMPTY;
// Skip leading space.
skip_space(&token);
// token += strspn(token, " \t");
skip_space(&token); //(*token) += strspn((*token), " \t");
assert(token);
if (token[0] == '\0') { // empty line
@@ -1078,7 +978,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2;
float x = 0.0f, y = 0.0f, z = 0.0f;
float x, y, z;
parseFloat3(&x, &y, &z, &token);
command->vx = x;
command->vy = y;
@@ -1090,7 +990,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// normal
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
token += 3;
float x = 0.0f, y = 0.0f, z = 0.0f;
float x, y, z;
parseFloat3(&x, &y, &z, &token);
command->nx = x;
command->ny = y;
@@ -1102,7 +1002,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3;
float x = 0.0f, y = 0.0f;
float x, y;
parseFloat2(&x, &y, &token);
command->tx = x;
command->ty = y;
@@ -1113,12 +1013,19 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// face
if (token[0] == 'f' && IS_SPACE((token[1]))) {
token += 2;
// token += strspn(token, " \t");
skip_space(&token);
StackVector<index_t, 8> f;
StackVector<vertex_index, 8> f;
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);
f->push_back(vi);
@@ -1127,9 +1034,9 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
command->type = COMMAND_F;
if (triangulate) {
index_t i0 = f[0];
index_t i1(-1);
index_t i2 = f[1];
vertex_index i0 = f[0];
vertex_index i1(-1);
vertex_index i2 = f[1];
for (size_t k = 2; k < f->size(); k++) {
i1 = i2;
@@ -1246,28 +1153,27 @@ static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
return false;
}
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<material_t> *materials, const char *buf, size_t len,
const LoadOption &option) {
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
size_t len, const LoadOption& option)
{
attrib->vertices.clear();
attrib->normals.clear();
attrib->texcoords.clear();
attrib->indices.clear();
attrib->faces.clear();
attrib->face_num_verts.clear();
attrib->material_ids.clear();
shapes->clear();
if (len < 1) return false;
auto num_threads = (option.req_num_threads < 0)
? std::thread::hardware_concurrency()
: option.req_num_threads;
auto num_threads = (option.req_num_threads < 0) ? std::thread::hardware_concurrency()
: option.req_num_threads;
num_threads =
std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
if (option.verbose) {
std::cout << "# of threads = " << num_threads << std::endl;
}
if (option.verbose) {
std::cout << "# of threads = " << num_threads << std::endl;
}
auto t1 = std::chrono::high_resolution_clock::now();
@@ -1398,7 +1304,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
command_count[t].num_vt++;
} else if (command.type == COMMAND_F) {
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) {
@@ -1423,6 +1329,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
std::map<std::string, int> material_map;
std::vector<material_t> materials;
// Load material(if exits)
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);
if (ifs.good()) {
LoadMtl(&material_map, materials, &ifs);
LoadMtl(&material_map, &materials, &ifs);
// 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_vt = 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++) {
num_v += command_count[t].num_v;
num_vn += command_count[t].num_vn;
num_vt += command_count[t].num_vt;
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 << "# vn " << num_vn << 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->normals.resize(num_vn * 3);
attrib->texcoords.resize(num_vt * 2);
attrib->indices.resize(num_f);
attrib->face_num_verts.resize(num_indices);
attrib->material_ids.resize(num_indices);
attrib->faces.resize(num_f);
attrib->face_num_verts.resize(num_faces);
attrib->material_ids.resize(num_faces);
size_t v_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;
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;
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;
@@ -1550,20 +1456,17 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
t_count++;
} else if (commands[t][i].type == COMMAND_F) {
for (size_t k = 0; k < commands[t][i].f.size(); k++) {
index_t &vi = commands[t][i].f[k];
int vertex_index = fixIndex(vi.vertex_index, v_count);
int texcoord_index = fixIndex(vi.texcoord_index, t_count);
int normal_index = fixIndex(vi.normal_index, n_count);
attrib->indices[f_count + k] =
index_t(vertex_index, texcoord_index, normal_index);
}
for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) {
attrib->material_ids[face_count + k] = material_id;
attrib->face_num_verts[face_count + k] = commands[t][i].f_num_verts[k];
vertex_index &vi = commands[t][i].f[k];
int v_idx = fixIndex(vi.v_idx, v_count);
int vn_idx = fixIndex(vi.vn_idx, n_count);
int vt_idx = fixIndex(vi.vt_idx, t_count);
attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
}
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();
face_count += commands[t][i].f_num_verts.size();
face_count++;
}
}
}));
@@ -1653,21 +1556,21 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
std::chrono::duration<double, std::milli> ms_total = t4 - t1;
if (option.verbose) {
std::cout << "total parsing time: " << ms_total.count() << " ms\n";
std::cout << " line detection : " << ms_linedetection.count() << " ms\n";
std::cout << " alloc buf : " << ms_alloc.count() << " ms\n";
std::cout << " parse : " << ms_parse.count() << " ms\n";
std::cout << " merge : " << ms_merge.count() << " ms\n";
std::cout << " construct : " << ms_construct.count() << " ms\n";
std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n";
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
std::cout << "# of normals = " << attrib->normals.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 indices = " << attrib->material_ids.size() << std::endl;
std::cout << "# of shapes = " << shapes->size() << std::endl;
}
if (option.verbose) {
std::cout << "total parsing time: " << ms_total.count() << " ms\n";
std::cout << " line detection : " << ms_linedetection.count() << " ms\n";
std::cout << " alloc buf : " << ms_alloc.count() << " ms\n";
std::cout << " parse : " << ms_parse.count() << " ms\n";
std::cout << " merge : " << ms_merge.count() << " ms\n";
std::cout << " construct : " << ms_construct.count() << " ms\n";
std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n";
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
std::cout << "# of normals = " << attrib->normals.size() << std::endl;
std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl;
std::cout << "# of face indices = " << attrib->faces.size() << std::endl;
std::cout << "# of faces = " << attrib->material_ids.size() << std::endl;
std::cout << "# of shapes = " << shapes->size() << std::endl;
}
return true;
}

View File

@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
}
static void vcopy(const float *v1, float *v2) {
int i;
register int i;
for (i = 0; i < 3; 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;
std::vector<tinyobj_opt::shape_t> shapes;
std::vector<tinyobj_opt::material_t> materials;
size_t data_len = 0;
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);
tinyobj_opt::LoadOption option;
option.req_num_threads = num_threads;
option.verbose = verbose;
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
bmin[0] = bmin[1] = bmin[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;
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++) {
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++) {
tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0];
tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1];
tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2];
tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0];
tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1];
tinyobj_opt::vertex_index idx2 = attrib.faces[face_offset+3*f+2];
float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
int f0 = idx0.v_idx;
int f1 = idx1.v_idx;
int f2 = idx2.v_idx;
assert(f0 >= 0);
assert(f1 >= 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];
if (attrib.normals.size() > 0) {
int nf0 = idx0.normal_index;
int nf1 = idx1.normal_index;
int nf2 = idx2.normal_index;
if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) {
assert(3*nf0+2 < attrib.normals.size());
assert(3*nf1+2 < attrib.normals.size());
assert(3*nf2+2 < attrib.normals.size());
for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3*nf0+k];
n[1][k] = attrib.normals[3*nf1+k];
n[2][k] = attrib.normals[3*nf2+k];
}
} else {
// 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];
int f0 = idx0.vn_idx;
int f1 = idx1.vn_idx;
int f2 = idx2.vn_idx;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
assert(3*f0+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++) {
n[0][k] = attrib.normals[3*f0+k];
n[1][k] = attrib.normals[3*f1+k];
n[2][k] = attrib.normals[3*f2+k];
}
} else {
// compute geometric normal
@@ -308,7 +299,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
// Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 1.0e-6f) {
if (len2 > 0.0f) {
float len = sqrtf(len2);
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]);
return true;
#else
return false;
#endif
}
void reshapeFunc(GLFWwindow* window, int w, int h)
@@ -505,31 +499,24 @@ static void Init() {
int main(int argc, char **argv)
{
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;
}
bool benchmark_only = false;
int num_threads = -1;
bool verbose = false;
if (argc > 2) {
num_threads = atoi(argv[2]);
}
if (argc > 3) {
benchmark_only = (atoi(argv[3]) > 0) ? true : false;
}
if (argc > 4) {
verbose = true;
benchmark_only = true;
}
if (benchmark_only) {
tinyobj_opt::attrib_t attrib;
std::vector<tinyobj_opt::shape_t> shapes;
std::vector<tinyobj_opt::material_t> materials;
size_t data_len = 0;
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);
tinyobj_opt::LoadOption option;
option.req_num_threads = num_threads;
option.verbose = true;
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
return ret;
}
@@ -584,7 +569,7 @@ int main(int argc, char **argv)
reshapeFunc(window, width, height);
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");
return -1;
}

View File

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

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,191 +1,160 @@
// python3 module for tinyobjloader
//python3 module for tinyobjloader
//
// usage:
//usage:
// import tinyobjloader as tol
// model = tol.LoadObj(name)
// print(model["shapes"])
// 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 <vector>
#include "../tiny_obj_loader.h"
typedef std::vector<double> vectd;
typedef std::vector<int> vecti;
PyObject* pyTupleFromfloat3(float array[3]) {
int i;
PyObject* tuple = PyTuple_New(3);
PyObject*
pyTupleFromfloat3 (float array[3])
{
int i;
PyObject* tuple = PyTuple_New(3);
for (i = 0; i <= 2; i++) {
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
}
for(i=0; i<=2 ; i++){
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
}
return tuple;
return tuple;
}
extern "C" {
extern "C"
{
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
PyObject *rtndict, *pyshapes, *pymaterials, *attribobj, *current, *meshobj;
static PyObject*
pyLoadObj(PyObject* self, PyObject* args)
{
PyObject *rtndict, *pyshapes, *pymaterials,
*current, *meshobj;
char const* current_name;
char const* filename;
vectd vect;
std::vector<tinyobj::index_t> indices;
std::vector<unsigned char> face_verts;
char const* filename;
char *current_name;
vectd vect;
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::vector<tinyobj::shape_t> shapes;
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;
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
std::string err;
tinyobj::LoadObj(shapes, materials, err, filename);
pyshapes = PyDict_New();
pymaterials = PyDict_New();
rtndict = PyDict_New();
attribobj = PyDict_New();
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();
tinyobj::mesh_t cm = (*shape).mesh;
pyshapes = PyDict_New();
pymaterials = PyDict_New();
rtndict = PyDict_New();
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin() ;
shape != shapes.end(); shape++)
{
current = PyList_New(0);
meshobj = PyDict_New();
tinyobj::mesh_t cm = (*shape).mesh;
for (size_t i = 0; i < cm.indices.size(); i++) {
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
// ...
PyList_Insert(current, 3 * i + 0,
PyLong_FromLong(cm.indices[i].vertex_index));
PyList_Insert(current, 3 * i + 1,
PyLong_FromLong(cm.indices[i].normal_index));
PyList_Insert(current, 3 * i + 2,
PyLong_FromLong(cm.indices[i].texcoord_index));
}
for (int i = 0; i <= 4; i++ )
{
current = PyList_New(0);
PyDict_SetItemString(meshobj, "indices", current);
switch(i) {
case 0:
current_name = "positions";
vect = vectd(cm.positions.begin(), cm.positions.end()); break;
case 1:
current_name = "normals";
vect = vectd(cm.normals.begin(), cm.normals.end()); break;
case 2:
current_name = "texcoords";
vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); break;
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++)
{
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
}
PyDict_SetItemString(meshobj, current_name, current);
}
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
}
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin() ;
mat != materials.end(); mat++)
{
current = PyList_New(0);
PyObject *matobj = PyDict_New();
PyObject *unknown_parameter = PyDict_New();
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]));
}
for (std::map<std::string, std::string>::iterator p = (*mat).unknown_parameter.begin() ;
p != (*mat).unknown_parameter.end(); ++p)
{
PyDict_SetItemString(unknown_parameter, p->first.c_str(), PyUnicode_FromString(p->second.c_str()));
}
PyDict_SetItemString(meshobj, "num_face_vertices", current);
PyDict_SetItemString(matobj, "shininess", PyFloat_FromDouble((*mat).shininess));
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
PyDict_SetItemString(matobj, "dissolve", PyFloat_FromDouble((*mat).dissolve));
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
PyDict_SetItemString(matobj, "ambient_texname", PyUnicode_FromString((*mat).ambient_texname.c_str()));
PyDict_SetItemString(matobj, "diffuse_texname", PyUnicode_FromString((*mat).diffuse_texname.c_str()));
PyDict_SetItemString(matobj, "specular_texname", PyUnicode_FromString((*mat).specular_texname.c_str()));
PyDict_SetItemString(matobj, "specular_highlight_texname", PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
PyDict_SetItemString(matobj, "bump_texname", PyUnicode_FromString((*mat).bump_texname.c_str()));
PyDict_SetItemString(matobj, "displacement_texname", PyUnicode_FromString((*mat).displacement_texname.c_str()));
PyDict_SetItemString(matobj, "alpha_texname", PyUnicode_FromString((*mat).alpha_texname.c_str()));
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3((*mat).specular));
PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3((*mat).transmittance));
PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3((*mat).emission));
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
}
{
current = PyList_New(0);
PyDict_SetItemString(rtndict, "shapes", pyshapes);
PyDict_SetItemString(rtndict, "materials", pymaterials);
for (size_t i = 0; i < cm.material_ids.size(); i++) {
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i]));
}
PyDict_SetItemString(meshobj, "material_ids", current);
}
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
}
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
mat != materials.end(); mat++) {
PyObject* matobj = PyDict_New();
PyObject* unknown_parameter = PyDict_New();
for (std::map<std::string, std::string>::iterator p =
(*mat).unknown_parameter.begin();
p != (*mat).unknown_parameter.end(); ++p) {
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
PyUnicode_FromString(p->second.c_str()));
}
PyDict_SetItemString(matobj, "shininess",
PyFloat_FromDouble((*mat).shininess));
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
PyDict_SetItemString(matobj, "dissolve",
PyFloat_FromDouble((*mat).dissolve));
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
PyDict_SetItemString(matobj, "ambient_texname",
PyUnicode_FromString((*mat).ambient_texname.c_str()));
PyDict_SetItemString(matobj, "diffuse_texname",
PyUnicode_FromString((*mat).diffuse_texname.c_str()));
PyDict_SetItemString(matobj, "specular_texname",
PyUnicode_FromString((*mat).specular_texname.c_str()));
PyDict_SetItemString(
matobj, "specular_highlight_texname",
PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
PyDict_SetItemString(matobj, "bump_texname",
PyUnicode_FromString((*mat).bump_texname.c_str()));
PyDict_SetItemString(
matobj, "displacement_texname",
PyUnicode_FromString((*mat).displacement_texname.c_str()));
PyDict_SetItemString(matobj, "alpha_texname",
PyUnicode_FromString((*mat).alpha_texname.c_str()));
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
PyDict_SetItemString(matobj, "specular",
pyTupleFromfloat3((*mat).specular));
PyDict_SetItemString(matobj, "transmittance",
pyTupleFromfloat3((*mat).transmittance));
PyDict_SetItemString(matobj, "emission",
pyTupleFromfloat3((*mat).emission));
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
}
PyDict_SetItemString(rtndict, "shapes", pyshapes);
PyDict_SetItemString(rtndict, "materials", pymaterials);
return rtndict;
return rtndict;
}
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) {
return PyModule_Create(&moduledef);
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"tinyobjloader",
NULL,
-1,
mMethods
};
PyMODINIT_FUNC
PyInit_tinyobjloader(void)
{
return PyModule_Create(&moduledef);
}
}

View File

@@ -293,7 +293,7 @@ std::string matStream(
return true;
}
const char* gMtlBasePath = "../models/";
const char* gMtlBasePath = "../models";
TEST_CASE("cornell_box", "[Loader]") {
@@ -319,107 +319,10 @@ TEST_CASE("catmark_torus_creases0", "[Loader]") {
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]") {
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
int
main(

View File

@@ -24,6 +24,33 @@ THE SOFTWARE.
//
// 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_
#define TINY_OBJ_LOADER_H_
#include <map>
#include <string>
#include <vector>
#include <map>
namespace tinyobj {
@@ -64,22 +91,6 @@ typedef struct {
std::string bump_texname; // map_bump, bump
std::string displacement_texname; // disp
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;
} material_t;
@@ -101,11 +112,10 @@ typedef struct {
typedef struct {
std::vector<index_t> indices;
std::vector<unsigned char> num_face_vertices; // The number of vertices per
// face. 3 = polygon, 4 = quad,
// ... Up to 255.
std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag
std::vector<unsigned char>
num_face_vertices; // The number of vertices per face. 3 = polygon, 4 = quad, ... Up to 255.
std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag
} mesh_t;
typedef struct {
@@ -121,22 +131,14 @@ typedef struct {
} attrib_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, float w);
void (*vertex_cb)(void *user_data, float x, float y, float z);
void (*normal_cb)(void *user_data, float x, float y, float z);
// y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in
// `vt` line.
void (*texcoord_cb)(void *user_data, float x, float y, float z);
// called per 'f' line. num_indices is the number of face indices(e.g. 3 for
// triangle, 4 for quad)
// 0 will be passed for undefined index in index_t members.
void (*index_cb)(void *user_data, index_t *indices, int num_indices);
// `name` material name, `material_id` = the array index of material_t[]. -1
// if
void (*texcoord_cb)(void *user_data, float x, float y);
// -2147483648 will be passed for undefined index
void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx);
// `name` material name, `materialId` = the array index of material_t[]. -1 if
// 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.
void (*mtllib_cb)(void *user_data, const material_t *materials,
int num_materials);
@@ -153,6 +155,7 @@ typedef struct callback_t_ {
mtllib_cb(NULL),
group_cb(NULL),
object_cb(NULL) {}
} callback_t;
class MaterialReader {
@@ -197,11 +200,12 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
/// `callback.mtllib_cb`.
/// Returns true when loading .obj/.mtl become success.
/// Returns warning and error message into `err`
/// See `examples/callback_api/` for how to use this function.
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
void *user_data = NULL,
MaterialReader *readMatFn = NULL,
std::string *err = NULL);
/// 'mtl_basepath' is optional, and used for base path for .mtl file.
/// 'triangulate' is optional, and used whether triangulate polygon face in .obj
/// or not.
bool LoadObjWithCallback(void *user_data, const callback_t &callback,
std::string *err, std::istream *inStream,
MaterialReader *readMatFn);
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
/// std::istream for materials.
@@ -219,12 +223,12 @@ void LoadMtl(std::map<std::string, int> *material_map,
} // namespace tinyobj
#ifdef TINYOBJLOADER_IMPLEMENTATION
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cctype>
#include <utility>
#include <fstream>
@@ -257,38 +261,6 @@ struct obj_shape {
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_DIGIT(x) \
(static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
@@ -452,13 +424,18 @@ fail:
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");
#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");
double val = default_value;
double val = 0.0;
tryParseDouble((*token), end, &val);
float f = static_cast<float>(val);
(*token) = end;
#endif
return f;
}
@@ -474,14 +451,6 @@ static inline void parseFloat3(float *x, float *y, float *z,
(*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) {
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
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));
(*token) += strcspn((*token), "/ \t\r");
@@ -592,20 +562,6 @@ static void InitMaterial(material_t *material) {
material->dissolve = 1.f;
material->shininess = 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();
}
@@ -660,8 +616,7 @@ static bool exportFaceGroupToShape(
shape->mesh.indices.push_back(idx);
}
shape->mesh.num_face_vertices.push_back(
static_cast<unsigned char>(npolys));
shape->mesh.num_face_vertices.push_back(static_cast<unsigned char>(npolys));
shape->mesh.material_ids.push_back(material_id); // per face
}
}
@@ -678,15 +633,12 @@ void LoadMtl(std::map<std::string, int> *material_map,
material_t material;
InitMaterial(&material);
size_t maxchars = 8192; // Alloc enough size.
std::vector<char> buf(maxchars); // Alloc enough size.
while (inStream->peek() != -1) {
std::string linebuf;
inStream->getline(&buf[0], static_cast<std::streamsize>(maxchars));
safeGetline(*inStream, linebuf);
// Trim trailing whitespace.
if (linebuf.size() > 0) {
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
}
std::string linebuf(&buf[0]);
// Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) {
@@ -770,8 +722,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
}
// transmittance
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 2;
float r, g, b;
parseFloat3(&r, &g, &b, &token);
@@ -826,55 +777,6 @@ void LoadMtl(std::map<std::string, int> *material_map,
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
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7;
@@ -931,41 +833,6 @@ void LoadMtl(std::map<std::string, int> *material_map,
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
const char *_space = strchr(token, ' ');
if (!_space) {
@@ -1059,9 +926,12 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
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) {
std::string linebuf;
safeGetline(*inStream, linebuf);
inStream->getline(&buf[0], maxchars);
std::string linebuf(&buf[0]);
// Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) {
@@ -1323,26 +1193,21 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
return true;
}
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
void *user_data /*= NULL*/,
MaterialReader *readMatFn /*= NULL*/,
std::string *err /*= NULL*/) {
bool LoadObjWithCallback(void *user_data, const callback_t &callback,
std::string *err, std::istream *inStream,
MaterialReader *readMatFn) {
std::stringstream errss;
// material
std::map<std::string, int> material_map;
int material_id = -1; // -1 = invalid
int materialId = -1; // -1 = invalid
std::vector<index_t> indices;
std::vector<material_t> materials;
std::vector<std::string> names;
names.reserve(2);
std::string name;
std::vector<const char *> names_out;
int maxchars = 8192; // Alloc enough size.
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
while (inStream->peek() != -1) {
inStream->getline(&buf[0], maxchars);
std::string linebuf;
while (inStream.peek() != -1) {
safeGetline(inStream, linebuf);
std::string linebuf(&buf[0]);
// Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) {
@@ -1371,10 +1236,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2;
float x, y, z, w; // w is optional. default = 1.0
parseV(&x, &y, &z, &w, &token);
float x, y, z;
parseFloat3(&x, &y, &z, &token);
if (callback.vertex_cb) {
callback.vertex_cb(user_data, x, y, z, w);
callback.vertex_cb(user_data, x, y, z);
}
continue;
}
@@ -1393,10 +1258,10 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3;
float x, y, z; // y and z are optional. default = 0.0
parseFloat3(&x, &y, &z, &token);
float x, y;
parseFloat2(&x, &y, &token);
if (callback.texcoord_cb) {
callback.texcoord_cb(user_data, x, y, z);
callback.texcoord_cb(user_data, x, y);
}
continue;
}
@@ -1406,25 +1271,15 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
token += 2;
token += strspn(token, " \t");
indices.clear();
while (!IS_NEW_LINE(token[0])) {
vertex_index vi = parseRawTriple(&token);
index_t idx;
idx.vertex_index = vi.v_idx;
idx.normal_index = vi.vn_idx;
idx.texcoord_index = vi.vt_idx;
indices.push_back(idx);
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;
}
if (callback.index_cb && indices.size() > 0) {
callback.index_cb(user_data, &indices.at(0),
static_cast<int>(indices.size()));
}
continue;
}
@@ -1433,8 +1288,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7;
#ifdef _MSC_VER
sscanf_s(token, "%s", namebuf,
static_cast<unsigned int>(_countof(namebuf)));
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
#else
sscanf(token, "%s", namebuf);
#endif
@@ -1446,12 +1300,12 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// { error!! material not found }
}
if (newMaterialId != material_id) {
material_id = newMaterialId;
if (newMaterialId != materialId) {
materialId = newMaterialId;
}
if (callback.usemtl_cb) {
callback.usemtl_cb(user_data, namebuf, material_id);
callback.usemtl_cb(user_data, namebuf, materialId);
}
continue;
@@ -1459,30 +1313,28 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// load mtl
if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
if (readMatFn) {
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7;
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 7;
#ifdef _MSC_VER
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
#else
sscanf(token, "%s", namebuf);
sscanf(token, "%s", namebuf);
#endif
std::string err_mtl;
materials.clear();
bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
if (err) {
(*err) += err_mtl;
}
std::string err_mtl;
std::vector<material_t> materials;
bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
if (err) {
(*err) += err_mtl;
}
if (!ok) {
return false;
}
if (!ok) {
return false;
}
if (callback.mtllib_cb) {
callback.mtllib_cb(user_data, &materials.at(0),
static_cast<int>(materials.size()));
}
if (callback.mtllib_cb) {
callback.mtllib_cb(user_data, &materials.at(0),
static_cast<int>(materials.size()));
}
continue;
@@ -1490,7 +1342,8 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
// group name
if (token[0] == 'g' && IS_SPACE((token[1]))) {
names.clear();
std::vector<std::string> names;
names.reserve(2);
while (!IS_NEW_LINE(token[0])) {
std::string str = parseString(&token);
@@ -1500,49 +1353,51 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
assert(names.size() > 0);
std::string name;
// names[0] must be 'g', so skip the 0th element.
if (names.size() > 1) {
name = names[1];
} else {
name.clear();
name = "";
}
if (callback.group_cb) {
if (names.size() > 1) {
// create const char* array.
names_out.resize(names.size() - 1);
for (size_t j = 0; j < names_out.size(); j++) {
names_out[j] = names[j + 1].c_str();
std::vector<const char *> tmp(names.size() - 1);
for (size_t j = 0; j < tmp.size(); j++) {
tmp[j] = names[j + 1].c_str();
}
callback.group_cb(user_data, &names_out.at(0),
static_cast<int>(names_out.size()));
callback.group_cb(user_data, &tmp.at(0),
static_cast<int>(tmp.size()));
} else {
callback.group_cb(user_data, NULL, 0);
}
}
continue;
}
}
// object name
if (token[0] == 'o' && IS_SPACE((token[1]))) {
// @todo { multiple object name? }
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 2;
// object name
if (token[0] == 'o' && IS_SPACE((token[1]))) {
// @todo { multiple object name? }
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
token += 2;
#ifdef _MSC_VER
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
#else
sscanf(token, "%s", namebuf);
sscanf(token, "%s", namebuf);
#endif
std::string object_name = std::string(namebuf);
std::string name = std::string(namebuf);
if (callback.object_cb) {
callback.object_cb(user_data, object_name.c_str());
}
if (callback.object_cb) {
callback.object_cb(user_data, name.c_str());
}
continue;
}
continue;
}
#if 0 // @todo
if (token[0] == 't' && IS_SPACE(token[1])) {