Compare commits
65 Commits
c006401ea4
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad79762212 | ||
|
|
64149943cc | ||
|
|
714194d353 | ||
|
|
9d81c24934 | ||
|
|
25c194bf71 | ||
|
|
5a4c5ff668 | ||
|
|
f06e814d7c | ||
|
|
5eef2b0914 | ||
|
|
49988672f4 | ||
|
|
d192402800 | ||
|
|
646f1312f1 | ||
|
|
db62a6c1cc | ||
|
|
8e53519a27 | ||
|
|
d3d6932efd | ||
|
|
dea325cdcb | ||
|
|
2b50b31657 | ||
|
|
110b49a0c8 | ||
|
|
319746d3db | ||
|
|
2cb73fa85d | ||
|
|
5a832b470a | ||
|
|
42f04024d4 | ||
|
|
07852e206d | ||
|
|
156b47760d | ||
|
|
673501749f | ||
|
|
b56fc1a0cc | ||
|
|
2ed3222bbe | ||
|
|
c7da23795d | ||
|
|
8ca2bc0de7 | ||
|
|
0a85945767 | ||
|
|
51d13700d8 | ||
|
|
1983e889dc | ||
|
|
dfe9d7bcae | ||
|
|
4dee4cc673 | ||
|
|
a7ea651bef | ||
|
|
4c2afb8814 | ||
|
|
951833812a | ||
|
|
e456cc949d | ||
|
|
75e64cd47a | ||
|
|
3736a5791f | ||
|
|
0b0bf60137 | ||
|
|
d496d8eab6 | ||
|
|
22883def8d | ||
|
|
5826f1e149 | ||
|
|
ef6560298e | ||
|
|
e3a56816d6 | ||
|
|
5ef400882a | ||
|
|
333bb55d84 | ||
|
|
7d6318c3ad | ||
|
|
d5c722125a | ||
|
|
16ed0ac129 | ||
|
|
b69d2a2c55 | ||
|
|
e210379335 | ||
|
|
659976b8f3 | ||
|
|
96ba498d70 | ||
|
|
7ecb0b2f37 | ||
|
|
a14bbdb065 | ||
|
|
bfedfbb1fb | ||
|
|
41f46c7fd7 | ||
|
|
a62dd278e2 | ||
|
|
a20e4ede85 | ||
|
|
1ab0d147cb | ||
|
|
9aee576b99 | ||
|
|
0dcc72239d | ||
|
|
33d5e9aa07 | ||
|
|
e528741a8b |
76
README.md
76
README.md
@@ -1,5 +1,4 @@
|
||||
tinyobjloader
|
||||
=============
|
||||
# tinyobjloader
|
||||
|
||||
[](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
@@ -17,29 +16,24 @@ Tiny but powerful single file wavefront obj loader written in C++. No dependency
|
||||
|
||||
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
|
||||
|
||||
If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c .
|
||||
|
||||
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
|
||||
Notice!
|
||||
-------
|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
tinyobjloader can successfully load 6M triangles Rungholt scene.
|
||||
@@ -48,12 +42,20 @@ http://graphics.cs.williams.edu/data/meshes.xml
|
||||

|
||||
|
||||
* [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
|
||||
@@ -65,10 +67,14 @@ 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
|
||||
* Your project here!
|
||||
* voxelizer https://github.com/karimnaaji/voxelizer
|
||||
* Probulator https://github.com/kayru/Probulator
|
||||
* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking
|
||||
* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK
|
||||
* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg
|
||||
* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl
|
||||
|
||||
Features
|
||||
--------
|
||||
## Features
|
||||
|
||||
* Group(parse multiple group name)
|
||||
* Vertex
|
||||
@@ -80,20 +86,16 @@ Features
|
||||
* 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`.
|
||||
@@ -148,8 +150,7 @@ for (size_t s = 0; s < shapes.size(); s++) {
|
||||
|
||||
```
|
||||
|
||||
Optimized loader
|
||||
----------------
|
||||
## Optimized loader
|
||||
|
||||
Optimized multi-threaded .obj loader is available at `experimental/` directory.
|
||||
If you want absolute performance to load .obj data, this optimized loader will fit your purpose.
|
||||
@@ -163,7 +164,6 @@ Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core
|
||||
* optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline)
|
||||
|
||||
|
||||
Tests
|
||||
-----
|
||||
## Tests
|
||||
|
||||
Unit tests are provided in `tests` directory. See `tests/README.md` for details.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 0.9.{build}
|
||||
version: 1.0.{build}
|
||||
|
||||
platform: x64
|
||||
|
||||
@@ -7,7 +7,6 @@ install:
|
||||
# All external dependencies are installed in C:\projects\deps
|
||||
#######################################################################################
|
||||
- mkdir C:\projects\deps
|
||||
- cd C:\projects\deps
|
||||
|
||||
#######################################################################################
|
||||
# Install Ninja
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
//
|
||||
// An example of how to use callback API.
|
||||
// This example is minimum and incomplete. Just showing the usage of callback
|
||||
// API.
|
||||
// You need to implement your own Mesh data struct constrution based on this
|
||||
// example in practical.
|
||||
//
|
||||
#define TINYOBJLOADER_IMPLEMENTATION
|
||||
#include "tiny_obj_loader.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#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)
|
||||
{
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("v[%ld] = %f, %f, %f\n", mesh->vertices.size() / 3, x, y, z);
|
||||
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);
|
||||
|
||||
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);
|
||||
@@ -41,49 +46,56 @@ 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)
|
||||
{
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||
printf("vt[%ld] = %f, %f\n", mesh->texcoords.size() / 2, x, y);
|
||||
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);
|
||||
|
||||
mesh->texcoords.push_back(x);
|
||||
mesh->texcoords.push_back(y);
|
||||
mesh->texcoords.push_back(z);
|
||||
}
|
||||
|
||||
void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx)
|
||||
{
|
||||
// NOTE: the value of each index is raw value.
|
||||
void index_cb(void *user_data, tinyobj::index_t *indices, int num_indices) {
|
||||
// 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(relative index).
|
||||
// (e.g. v_indices.size()) when the value is negative(whic means relative
|
||||
// index).
|
||||
// Also, the first index starts with 1, not 0.
|
||||
// See fixIndex() function in tiny_obj_loader.h for details.
|
||||
// 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);
|
||||
// Also, 0 is set for the index value which
|
||||
// does not exist in .obj
|
||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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++) {
|
||||
@@ -91,9 +103,8 @@ void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_ma
|
||||
}
|
||||
}
|
||||
|
||||
void group_cb(void *user_data, const char **names, int num_names)
|
||||
{
|
||||
//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++) {
|
||||
@@ -101,16 +112,12 @@ void group_cb(void *user_data, const char **names, int num_names)
|
||||
}
|
||||
}
|
||||
|
||||
void object_cb(void *user_data, const char *name)
|
||||
{
|
||||
//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;
|
||||
@@ -123,7 +130,11 @@ main(int argc, char** argv)
|
||||
|
||||
MyMesh mesh;
|
||||
std::string err;
|
||||
std::ifstream ifs("../../models/cornell_box.obj");
|
||||
std::string filename = "../../models/cornell_box.obj";
|
||||
if (argc > 1) {
|
||||
filename = std::string(argv[1]);
|
||||
}
|
||||
std::ifstream ifs(filename.c_str());
|
||||
|
||||
if (ifs.fail()) {
|
||||
std::cerr << "file not found." << std::endl;
|
||||
@@ -131,8 +142,8 @@ main(int argc, char** argv)
|
||||
}
|
||||
|
||||
tinyobj::MaterialFileReader mtlReader("../../models/");
|
||||
|
||||
bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader);
|
||||
|
||||
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//
|
||||
// Simple .obj viewer(vertex only)
|
||||
//
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <windows.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 @@ public:
|
||||
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 @@ public:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
DWORD t_[2];
|
||||
#else
|
||||
@@ -110,7 +110,7 @@ private:
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
GLuint vb; // vertex buffer
|
||||
GLuint vb; // vertex buffer
|
||||
int numTriangles;
|
||||
} DrawObject;
|
||||
|
||||
@@ -155,14 +155,15 @@ 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;
|
||||
@@ -170,21 +171,22 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
||||
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", tm.msec());
|
||||
printf("Parsing time: %d [ms]\n", (int)tm.msec());
|
||||
|
||||
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
|
||||
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
|
||||
@@ -196,102 +198,105 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
||||
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++) {
|
||||
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];
|
||||
|
||||
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];
|
||||
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);
|
||||
|
||||
float v[3][3];
|
||||
for (int k = 0; k < 3; k++) {
|
||||
int f0 = idx0.vertex_index;
|
||||
int f1 = idx1.vertex_index;
|
||||
int f2 = idx2.vertex_index;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
|
||||
v[0][k] = attrib.vertices[3*f0+k];
|
||||
v[1][k] = attrib.vertices[3*f1+k];
|
||||
v[2][k] = attrib.vertices[3*f2+k];
|
||||
bmin[k] = std::min(v[0][k], bmin[k]);
|
||||
bmin[k] = std::min(v[1][k], bmin[k]);
|
||||
bmin[k] = std::min(v[2][k], bmin[k]);
|
||||
bmax[k] = std::max(v[0][k], bmax[k]);
|
||||
bmax[k] = std::max(v[1][k], bmax[k]);
|
||||
bmax[k] = std::max(v[2][k], bmax[k]);
|
||||
}
|
||||
|
||||
float n[3][3];
|
||||
|
||||
if (attrib.normals.size() > 0) {
|
||||
int f0 = idx0.normal_index;
|
||||
int f1 = idx1.normal_index;
|
||||
int f2 = idx2.normal_index;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
for (int k = 0; k < 3; k++) {
|
||||
n[0][k] = attrib.normals[3*f0+k];
|
||||
n[1][k] = attrib.normals[3*f1+k];
|
||||
n[2][k] = attrib.normals[3*f2+k];
|
||||
}
|
||||
} else {
|
||||
// compute geometric normal
|
||||
CalcNormal(n[0], v[0], v[1], v[2]);
|
||||
n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2];
|
||||
n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2];
|
||||
}
|
||||
|
||||
for (int k = 0; k < 3; k++) {
|
||||
vb.push_back(v[k][0]);
|
||||
vb.push_back(v[k][1]);
|
||||
vb.push_back(v[k][2]);
|
||||
vb.push_back(n[k][0]);
|
||||
vb.push_back(n[k][1]);
|
||||
vb.push_back(n[k][2]);
|
||||
// Use normal as color.
|
||||
float c[3] = {n[k][0], n[k][1], n[k][2]};
|
||||
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
|
||||
if (len2 > 0.0f) {
|
||||
float len = sqrtf(len2);
|
||||
|
||||
c[0] /= len;
|
||||
c[1] /= len;
|
||||
c[2] /= len;
|
||||
}
|
||||
vb.push_back(c[0] * 0.5 + 0.5);
|
||||
vb.push_back(c[1] * 0.5 + 0.5);
|
||||
vb.push_back(c[2] * 0.5 + 0.5);
|
||||
}
|
||||
|
||||
v[0][k] = attrib.vertices[3 * f0 + k];
|
||||
v[1][k] = attrib.vertices[3 * f1 + k];
|
||||
v[2][k] = attrib.vertices[3 * f2 + k];
|
||||
bmin[k] = std::min(v[0][k], bmin[k]);
|
||||
bmin[k] = std::min(v[1][k], bmin[k]);
|
||||
bmin[k] = std::min(v[2][k], bmin[k]);
|
||||
bmax[k] = std::max(v[0][k], bmax[k]);
|
||||
bmax[k] = std::max(v[1][k], bmax[k]);
|
||||
bmax[k] = std::max(v[2][k], bmax[k]);
|
||||
}
|
||||
|
||||
o.vb = 0;
|
||||
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);
|
||||
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];
|
||||
}
|
||||
|
||||
gDrawObjects.push_back(o);
|
||||
for (int k = 0; k < 3; k++) {
|
||||
vb.push_back(v[k][0]);
|
||||
vb.push_back(v[k][1]);
|
||||
vb.push_back(v[k][2]);
|
||||
vb.push_back(n[k][0]);
|
||||
vb.push_back(n[k][1]);
|
||||
vb.push_back(n[k][2]);
|
||||
// Use normal as color.
|
||||
float c[3] = {n[k][0], n[k][1], n[k][2]};
|
||||
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
|
||||
if (len2 > 0.0f) {
|
||||
float len = sqrtf(len2);
|
||||
|
||||
c[0] /= len;
|
||||
c[1] /= len;
|
||||
c[2] /= len;
|
||||
}
|
||||
vb.push_back(c[0] * 0.5 + 0.5);
|
||||
vb.push_back(c[1] * 0.5 + 0.5);
|
||||
vb.push_back(c[2] * 0.5 + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
o.vb = 0;
|
||||
o.numTriangles = 0;
|
||||
if (vb.size() > 0) {
|
||||
glGenBuffers(1, &o.vb);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
|
||||
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0),
|
||||
GL_STATIC_DRAW);
|
||||
o.numTriangles = vb.size() / 9 / 3;
|
||||
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
|
||||
o.numTriangles);
|
||||
}
|
||||
|
||||
drawObjects->push_back(o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
@@ -307,78 +312,90 @@ void reshapeFunc(GLFWwindow* window, int w, int h)
|
||||
height = h;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
//init_frame = true;
|
||||
}
|
||||
// init_frame = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
|
||||
(void)window;
|
||||
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);
|
||||
|
||||
@@ -390,14 +407,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");
|
||||
@@ -414,13 +431,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");
|
||||
@@ -443,26 +460,21 @@ 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;
|
||||
@@ -486,7 +498,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;
|
||||
}
|
||||
|
||||
@@ -498,7 +510,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);
|
||||
@@ -509,7 +521,8 @@ 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]);
|
||||
|
||||
@@ -517,8 +530,9 @@ 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);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
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
@@ -292,6 +292,22 @@ 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;
|
||||
|
||||
@@ -301,19 +317,20 @@ typedef struct {
|
||||
unsigned int length;
|
||||
} shape_t;
|
||||
|
||||
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) {}
|
||||
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) {}
|
||||
};
|
||||
|
||||
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<vertex_index, lt::allocator<vertex_index> > faces;
|
||||
std::vector<index_t, lt::allocator<index_t> > indices;
|
||||
std::vector<int, lt::allocator<int> > face_num_verts;
|
||||
std::vector<int, lt::allocator<int> > material_ids;
|
||||
} attrib_t;
|
||||
@@ -386,12 +403,11 @@ static inline int fixIndex(int idx, int n) {
|
||||
}
|
||||
|
||||
// Parse raw triples: i, i/j/k, i//k, i/j
|
||||
static vertex_index parseRawTriple(const char **token) {
|
||||
vertex_index vi(
|
||||
static index_t parseRawTriple(const char **token) {
|
||||
index_t vi(
|
||||
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
|
||||
|
||||
vi.v_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.vertex_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -404,8 +420,7 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
// i//k
|
||||
if ((*token)[0] == '/') {
|
||||
(*token)++;
|
||||
vi.vn_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.normal_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -414,8 +429,7 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
}
|
||||
|
||||
// i/j/k or i/j
|
||||
vi.vt_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.texcoord_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -426,8 +440,7 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
|
||||
// i/j/k
|
||||
(*token)++; // skip '/'
|
||||
vi.vn_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
vi.normal_index = my_atoi((*token));
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
@@ -436,17 +449,17 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
}
|
||||
|
||||
static inline bool parseString(ShortString *s, const char **token) {
|
||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
||||
size_t e = until_space((*token)); // strcspn((*token), " \t\r");
|
||||
skip_space(token);
|
||||
size_t e = until_space((*token));
|
||||
(*s)->insert((*s)->end(), (*token), (*token) + e);
|
||||
(*token) += e;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int parseInt(const char **token) {
|
||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
||||
skip_space(token);
|
||||
int i = my_atoi((*token));
|
||||
(*token) += until_space((*token)); // strcspn((*token), " \t\r");
|
||||
(*token) += until_space((*token));
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -585,9 +598,9 @@ assemble :
|
||||
|
||||
{
|
||||
// = pow(5.0, exponent);
|
||||
double a = 5.0;
|
||||
double a = 1.0;
|
||||
for (int i = 0; i < exponent; i++) {
|
||||
a = a * a;
|
||||
a = a * 5.0;
|
||||
}
|
||||
*result =
|
||||
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
|
||||
@@ -601,13 +614,12 @@ fail:
|
||||
}
|
||||
|
||||
static inline float parseFloat(const char **token) {
|
||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
||||
skip_space(token);
|
||||
#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)); // strcspn((*token), " \t\r");
|
||||
const char *end = (*token) + until_space((*token));
|
||||
double val = 0.0;
|
||||
tryParseDouble((*token), end, &val);
|
||||
float f = static_cast<float>(val);
|
||||
@@ -665,6 +677,11 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
|
||||
std::string linebuf(&buf[0]);
|
||||
|
||||
// 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')
|
||||
@@ -747,7 +764,8 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
}
|
||||
|
||||
// transmittance
|
||||
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
|
||||
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
|
||||
token += 2;
|
||||
float r, g, b;
|
||||
parseFloat3(&r, &g, &b, &token);
|
||||
@@ -795,6 +813,7 @@ 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])
|
||||
@@ -802,6 +821,55 @@ 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;
|
||||
@@ -858,6 +926,41 @@ 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) {
|
||||
@@ -896,8 +999,8 @@ typedef struct {
|
||||
float tx, ty;
|
||||
|
||||
// for f
|
||||
std::vector<vertex_index, lt::allocator<vertex_index> > f;
|
||||
// std::vector<vertex_index> f;
|
||||
std::vector<index_t, lt::allocator<index_t> > f;
|
||||
// std::vector<index_t> f;
|
||||
std::vector<int, lt::allocator<int> > f_num_verts;
|
||||
|
||||
const char *group_name;
|
||||
@@ -918,44 +1021,42 @@ struct CommandCount {
|
||||
size_t num_vn;
|
||||
size_t num_vt;
|
||||
size_t num_f;
|
||||
size_t num_faces;
|
||||
size_t num_indices;
|
||||
CommandCount() {
|
||||
num_v = 0;
|
||||
num_vn = 0;
|
||||
num_vt = 0;
|
||||
num_f = 0;
|
||||
num_faces = 0;
|
||||
num_indices = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class
|
||||
LoadOption
|
||||
{
|
||||
class LoadOption {
|
||||
public:
|
||||
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
|
||||
|
||||
int req_num_threads;
|
||||
bool triangulate;
|
||||
bool verbose;
|
||||
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
|
||||
|
||||
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, const char *buf,
|
||||
size_t len, const LoadOption& option);
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||
std::vector<material_t> *materials, 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);
|
||||
// StackVector<char, 256> linebuf;
|
||||
// linebuf->resize(p_len + 1);
|
||||
memcpy(&linebuf, p, p_len);
|
||||
memcpy(linebuf, p, p_len);
|
||||
linebuf[p_len] = '\0';
|
||||
|
||||
const char *token = linebuf;
|
||||
@@ -963,8 +1064,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
command->type = COMMAND_EMPTY;
|
||||
|
||||
// Skip leading space.
|
||||
// token += strspn(token, " \t");
|
||||
skip_space(&token); //(*token) += strspn((*token), " \t");
|
||||
skip_space(&token);
|
||||
|
||||
assert(token);
|
||||
if (token[0] == '\0') { // empty line
|
||||
@@ -978,7 +1078,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, y, z;
|
||||
float x = 0.0f, y = 0.0f, z = 0.0f;
|
||||
parseFloat3(&x, &y, &z, &token);
|
||||
command->vx = x;
|
||||
command->vy = y;
|
||||
@@ -990,7 +1090,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, y, z;
|
||||
float x = 0.0f, y = 0.0f, z = 0.0f;
|
||||
parseFloat3(&x, &y, &z, &token);
|
||||
command->nx = x;
|
||||
command->ny = y;
|
||||
@@ -1002,7 +1102,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, y;
|
||||
float x = 0.0f, y = 0.0f;
|
||||
parseFloat2(&x, &y, &token);
|
||||
command->tx = x;
|
||||
command->ty = y;
|
||||
@@ -1013,19 +1113,12 @@ 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<vertex_index, 8> f;
|
||||
StackVector<index_t, 8> f;
|
||||
|
||||
while (!IS_NEW_LINE(token[0])) {
|
||||
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;
|
||||
index_t vi = parseRawTriple(&token);
|
||||
skip_space_and_cr(&token);
|
||||
|
||||
f->push_back(vi);
|
||||
@@ -1034,9 +1127,9 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
command->type = COMMAND_F;
|
||||
|
||||
if (triangulate) {
|
||||
vertex_index i0 = f[0];
|
||||
vertex_index i1(-1);
|
||||
vertex_index i2 = f[1];
|
||||
index_t i0 = f[0];
|
||||
index_t i1(-1);
|
||||
index_t i2 = f[1];
|
||||
|
||||
for (size_t k = 2; k < f->size(); k++) {
|
||||
i1 = i2;
|
||||
@@ -1153,27 +1246,28 @@ 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, const char *buf,
|
||||
size_t len, const LoadOption& option)
|
||||
{
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||
std::vector<material_t> *materials, const char *buf, size_t len,
|
||||
const LoadOption &option) {
|
||||
attrib->vertices.clear();
|
||||
attrib->normals.clear();
|
||||
attrib->texcoords.clear();
|
||||
attrib->faces.clear();
|
||||
attrib->indices.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();
|
||||
|
||||
@@ -1304,7 +1398,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
command_count[t].num_vt++;
|
||||
} else if (command.type == COMMAND_F) {
|
||||
command_count[t].num_f += command.f.size();
|
||||
command_count[t].num_faces++;
|
||||
command_count[t].num_indices += command.f_num_verts.size();
|
||||
}
|
||||
|
||||
if (command.type == COMMAND_MTLLIB) {
|
||||
@@ -1329,7 +1423,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
}
|
||||
|
||||
std::map<std::string, int> material_map;
|
||||
std::vector<material_t> materials;
|
||||
|
||||
// Load material(if exits)
|
||||
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
|
||||
@@ -1344,7 +1437,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
|
||||
std::ifstream ifs(material_filename);
|
||||
if (ifs.good()) {
|
||||
LoadMtl(&material_map, &materials, &ifs);
|
||||
LoadMtl(&material_map, materials, &ifs);
|
||||
|
||||
// std::cout << "maetrials = " << materials.size() << std::endl;
|
||||
|
||||
@@ -1368,14 +1461,15 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
size_t num_vn = 0;
|
||||
size_t num_vt = 0;
|
||||
size_t num_f = 0;
|
||||
size_t num_faces = 0;
|
||||
size_t num_indices = 0;
|
||||
for (size_t t = 0; t < num_threads; t++) {
|
||||
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_faces += command_count[t].num_faces;
|
||||
num_indices += command_count[t].num_indices;
|
||||
}
|
||||
|
||||
// std::cout << "# v " << num_v << std::endl;
|
||||
// std::cout << "# vn " << num_vn << std::endl;
|
||||
// std::cout << "# vt " << num_vt << std::endl;
|
||||
@@ -1389,9 +1483,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
attrib->vertices.resize(num_v * 3);
|
||||
attrib->normals.resize(num_vn * 3);
|
||||
attrib->texcoords.resize(num_vt * 2);
|
||||
attrib->faces.resize(num_f);
|
||||
attrib->face_num_verts.resize(num_faces);
|
||||
attrib->material_ids.resize(num_faces);
|
||||
attrib->indices.resize(num_f);
|
||||
attrib->face_num_verts.resize(num_indices);
|
||||
attrib->material_ids.resize(num_indices);
|
||||
|
||||
size_t v_offsets[kMaxThreads];
|
||||
size_t n_offsets[kMaxThreads];
|
||||
@@ -1410,7 +1504,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn;
|
||||
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_faces;
|
||||
face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_indices;
|
||||
}
|
||||
|
||||
StackVector<std::thread, 16> workers;
|
||||
@@ -1456,17 +1550,20 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
t_count++;
|
||||
} else if (commands[t][i].type == COMMAND_F) {
|
||||
for (size_t k = 0; k < commands[t][i].f.size(); 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);
|
||||
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];
|
||||
}
|
||||
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++;
|
||||
face_count += commands[t][i].f_num_verts.size();
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -1556,21 +1653,21 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
}
|
||||
|
||||
std::chrono::duration<double, std::milli> ms_total = t4 - t1;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
|
||||
}
|
||||
|
||||
static void vcopy(const float *v1, float *v2) {
|
||||
register int i;
|
||||
int i;
|
||||
for (i = 0; i < 3; i++)
|
||||
v2[i] = v1[i];
|
||||
}
|
||||
@@ -1,622 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
@@ -214,11 +214,11 @@ const char* get_file_data(size_t *len, const char* filename)
|
||||
}
|
||||
|
||||
|
||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads)
|
||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose)
|
||||
{
|
||||
#if 1
|
||||
tinyobj_opt::attrib_t attrib;
|
||||
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,11 +229,15 @@ 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;
|
||||
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
|
||||
option.verbose = verbose;
|
||||
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
|
||||
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||
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)
|
||||
@@ -241,15 +245,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
|
||||
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::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];
|
||||
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];
|
||||
|
||||
float v[3][3];
|
||||
for (int k = 0; k < 3; k++) {
|
||||
int f0 = idx0.v_idx;
|
||||
int f1 = idx1.v_idx;
|
||||
int f2 = idx2.v_idx;
|
||||
int f0 = idx0.vertex_index;
|
||||
int f1 = idx1.vertex_index;
|
||||
int f2 = idx2.vertex_index;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
@@ -268,19 +272,24 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
float n[3][3];
|
||||
|
||||
if (attrib.normals.size() > 0) {
|
||||
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];
|
||||
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];
|
||||
}
|
||||
} else {
|
||||
// compute geometric normal
|
||||
@@ -299,7 +308,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 > 0.0f) {
|
||||
if (len2 > 1.0e-6f) {
|
||||
float len = sqrtf(len2);
|
||||
|
||||
c[0] /= len;
|
||||
@@ -330,9 +339,6 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void reshapeFunc(GLFWwindow* window, int w, int h)
|
||||
@@ -499,24 +505,31 @@ static void Init() {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
std::cout << "Needs input.obj\n" << std::endl;
|
||||
std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool benchmark_only = false;
|
||||
int num_threads = -1;
|
||||
bool verbose = false;
|
||||
|
||||
if (argc > 2) {
|
||||
num_threads = atoi(argv[2]);
|
||||
}
|
||||
|
||||
if (argc > 3) {
|
||||
benchmark_only = true;
|
||||
benchmark_only = (atoi(argv[3]) > 0) ? true : false;
|
||||
}
|
||||
|
||||
if (argc > 4) {
|
||||
verbose = 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]);
|
||||
@@ -527,7 +540,9 @@ int main(int argc, char **argv)
|
||||
printf("filesize: %d\n", (int)data_len);
|
||||
tinyobj_opt::LoadOption option;
|
||||
option.req_num_threads = num_threads;
|
||||
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
|
||||
option.verbose = true;
|
||||
|
||||
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -569,7 +584,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)) {
|
||||
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) {
|
||||
printf("failed to load & conv\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
#define TINYOBJLOADER_IMPLEMENTATION
|
||||
#include "tiny_obj_loader.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <windows.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -30,7 +30,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
class timerutil {
|
||||
public:
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
typedef DWORD time_t;
|
||||
|
||||
@@ -58,7 +58,8 @@ public:
|
||||
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;
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
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;
|
||||
|
||||
@@ -81,7 +82,7 @@ public:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
DWORD t_[2];
|
||||
#else
|
||||
@@ -94,96 +95,103 @@ private:
|
||||
#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");
|
||||
@@ -191,25 +199,59 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < materials.size(); i++) {
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
|
||||
for (; it != itEnd; it++) {
|
||||
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
|
||||
@@ -218,12 +260,8 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
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;
|
||||
@@ -233,7 +271,8 @@ TestLoadObj(
|
||||
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());
|
||||
|
||||
@@ -251,102 +290,96 @@ TestLoadObj(
|
||||
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;
|
||||
}
|
||||
@@ -356,15 +389,11 @@ std::string matStream(
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -372,10 +401,11 @@ main(
|
||||
}
|
||||
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;
|
||||
|
||||
6
models/issue-92.mtl
Normal file
6
models/issue-92.mtl
Normal file
@@ -0,0 +1,6 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
map_Kd tmp.png
|
||||
|
||||
7
models/issue-92.obj
Normal file
7
models/issue-92.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
mtllib issue-92.mtl
|
||||
o Test
|
||||
v 1.864151 -1.219172 -5.532511
|
||||
v 0.575869 -0.666304 5.896140
|
||||
v 0.940448 1.000000 -1.971128
|
||||
usemtl default
|
||||
f 1 2 3
|
||||
5
models/issue-95-2.mtl
Normal file
5
models/issue-95-2.mtl
Normal file
@@ -0,0 +1,5 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
Tf 0.1 0.2 0.3
|
||||
7
models/issue-95-2.obj
Normal file
7
models/issue-95-2.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
mtllib issue-95-2.mtl
|
||||
o Test
|
||||
v 1.864151 -1.219172 -5.532511
|
||||
v 0.575869 -0.666304 5.896140
|
||||
v 0.940448 1.000000 -1.971128
|
||||
usemtl default
|
||||
f 1 2 3
|
||||
5
models/issue-95.mtl
Normal file
5
models/issue-95.mtl
Normal file
@@ -0,0 +1,5 @@
|
||||
newmtl default
|
||||
Ka 0 0 0
|
||||
Kd 0 0 0
|
||||
Ks 0 0 0
|
||||
Kt 0.1 0.2 0.3
|
||||
7
models/issue-95.obj
Normal file
7
models/issue-95.obj
Normal file
@@ -0,0 +1,7 @@
|
||||
mtllib issue-95.mtl
|
||||
o Test
|
||||
v 1.864151 -1.219172 -5.532511
|
||||
v 0.575869 -0.666304 5.896140
|
||||
v 0.940448 1.000000 -1.971128
|
||||
usemtl default
|
||||
f 1 2 3
|
||||
19
models/pbr-mat-ext.mtl
Normal file
19
models/pbr-mat-ext.mtl
Normal file
@@ -0,0 +1,19 @@
|
||||
# .MTL with PBR extension.
|
||||
newmtl pbr
|
||||
Ka 0 0 0
|
||||
Kd 1 1 1
|
||||
Ks 0 0 0
|
||||
Ke 0.1 0.1 0.1
|
||||
Pr 0.2
|
||||
Pm 0.3
|
||||
Ps 0.4
|
||||
Pc 0.5
|
||||
Pcr 0.6
|
||||
aniso 0.7
|
||||
anisor 0.8
|
||||
map_Pr roughness.tex
|
||||
map_Pm metallic.tex
|
||||
map_Ps sheen.tex
|
||||
map_Ke emissive.tex
|
||||
norm normalmap.tex
|
||||
|
||||
10
models/pbr-mat-ext.obj
Normal file
10
models/pbr-mat-ext.obj
Normal file
@@ -0,0 +1,10 @@
|
||||
mtllib pbr-mat-ext.mtl
|
||||
|
||||
o floor
|
||||
usemtl pbr
|
||||
v 552.8 0.0 0.0
|
||||
v 0.0 0.0 0.0
|
||||
v 0.0 0.0 559.2
|
||||
v 549.6 0.0 559.2
|
||||
|
||||
f 1 2 3 4
|
||||
3
python/TODO.md
Normal file
3
python/TODO.md
Normal file
@@ -0,0 +1,3 @@
|
||||
* PBR material
|
||||
* Define index_t struct
|
||||
* Python 2.7 binding
|
||||
271
python/main.cpp
271
python/main.cpp
@@ -1,160 +1,191 @@
|
||||
//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,
|
||||
*current, *meshobj;
|
||||
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
|
||||
PyObject *rtndict, *pyshapes, *pymaterials, *attribobj, *current, *meshobj;
|
||||
|
||||
char const* filename;
|
||||
char *current_name;
|
||||
vectd vect;
|
||||
char const* current_name;
|
||||
char const* filename;
|
||||
vectd vect;
|
||||
std::vector<tinyobj::index_t> indices;
|
||||
std::vector<unsigned char> face_verts;
|
||||
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
tinyobj::attrib_t attrib;
|
||||
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(shapes, materials, err, filename);
|
||||
std::string err;
|
||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
|
||||
|
||||
pyshapes = PyDict_New();
|
||||
pymaterials = PyDict_New();
|
||||
rtndict = PyDict_New();
|
||||
pyshapes = PyDict_New();
|
||||
pymaterials = PyDict_New();
|
||||
rtndict = PyDict_New();
|
||||
|
||||
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin() ;
|
||||
shape != shapes.end(); shape++)
|
||||
{
|
||||
meshobj = PyDict_New();
|
||||
tinyobj::mesh_t cm = (*shape).mesh;
|
||||
attribobj = PyDict_New();
|
||||
|
||||
for (int i = 0; i <= 4; i++ )
|
||||
{
|
||||
current = PyList_New(0);
|
||||
for (int i = 0; i <= 2; i++) {
|
||||
current = PyList_New(0);
|
||||
|
||||
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);
|
||||
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 (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);
|
||||
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
|
||||
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(rtndict, "shapes", pyshapes);
|
||||
PyDict_SetItemString(rtndict, "materials", pymaterials);
|
||||
PyDict_SetItemString(attribobj, current_name, current);
|
||||
}
|
||||
|
||||
return rtndict;
|
||||
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
|
||||
shape != shapes.end(); shape++) {
|
||||
meshobj = PyDict_New();
|
||||
tinyobj::mesh_t cm = (*shape).mesh;
|
||||
|
||||
{
|
||||
current = PyList_New(0);
|
||||
|
||||
for (size_t i = 0; i < cm.indices.size(); i++) {
|
||||
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
|
||||
// ...
|
||||
PyList_Insert(current, 3 * i + 0,
|
||||
PyLong_FromLong(cm.indices[i].vertex_index));
|
||||
PyList_Insert(current, 3 * i + 1,
|
||||
PyLong_FromLong(cm.indices[i].normal_index));
|
||||
PyList_Insert(current, 3 * i + 2,
|
||||
PyLong_FromLong(cm.indices[i].texcoord_index));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, "indices", current);
|
||||
}
|
||||
|
||||
{
|
||||
current = PyList_New(0);
|
||||
|
||||
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
|
||||
// Widen data type to long.
|
||||
PyList_Insert(current, i, PyLong_FromLong(cm.num_face_vertices[i]));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, "num_face_vertices", current);
|
||||
}
|
||||
|
||||
{
|
||||
current = PyList_New(0);
|
||||
|
||||
for (size_t i = 0; i < cm.material_ids.size(); i++) {
|
||||
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i]));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(meshobj, "material_ids", current);
|
||||
}
|
||||
|
||||
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
|
||||
}
|
||||
|
||||
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
|
||||
mat != materials.end(); mat++) {
|
||||
PyObject* matobj = PyDict_New();
|
||||
PyObject* unknown_parameter = PyDict_New();
|
||||
|
||||
for (std::map<std::string, std::string>::iterator p =
|
||||
(*mat).unknown_parameter.begin();
|
||||
p != (*mat).unknown_parameter.end(); ++p) {
|
||||
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
|
||||
PyUnicode_FromString(p->second.c_str()));
|
||||
}
|
||||
|
||||
PyDict_SetItemString(matobj, "shininess",
|
||||
PyFloat_FromDouble((*mat).shininess));
|
||||
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
|
||||
PyDict_SetItemString(matobj, "dissolve",
|
||||
PyFloat_FromDouble((*mat).dissolve));
|
||||
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
|
||||
PyDict_SetItemString(matobj, "ambient_texname",
|
||||
PyUnicode_FromString((*mat).ambient_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "diffuse_texname",
|
||||
PyUnicode_FromString((*mat).diffuse_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "specular_texname",
|
||||
PyUnicode_FromString((*mat).specular_texname.c_str()));
|
||||
PyDict_SetItemString(
|
||||
matobj, "specular_highlight_texname",
|
||||
PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "bump_texname",
|
||||
PyUnicode_FromString((*mat).bump_texname.c_str()));
|
||||
PyDict_SetItemString(
|
||||
matobj, "displacement_texname",
|
||||
PyUnicode_FromString((*mat).displacement_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "alpha_texname",
|
||||
PyUnicode_FromString((*mat).alpha_texname.c_str()));
|
||||
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient));
|
||||
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
|
||||
PyDict_SetItemString(matobj, "specular",
|
||||
pyTupleFromfloat3((*mat).specular));
|
||||
PyDict_SetItemString(matobj, "transmittance",
|
||||
pyTupleFromfloat3((*mat).transmittance));
|
||||
PyDict_SetItemString(matobj, "emission",
|
||||
pyTupleFromfloat3((*mat).emission));
|
||||
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
||||
|
||||
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
|
||||
}
|
||||
|
||||
PyDict_SetItemString(rtndict, "shapes", pyshapes);
|
||||
PyDict_SetItemString(rtndict, "materials", pymaterials);
|
||||
|
||||
return rtndict;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef mMethods[] = {
|
||||
|
||||
{"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};
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"tinyobjloader",
|
||||
NULL,
|
||||
-1,
|
||||
mMethods
|
||||
};
|
||||
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_tinyobjloader(void)
|
||||
{
|
||||
return PyModule_Create(&moduledef);
|
||||
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
|
||||
return PyModule_Create(&moduledef);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ std::string matStream(
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* gMtlBasePath = "../models";
|
||||
const char* gMtlBasePath = "../models/";
|
||||
|
||||
TEST_CASE("cornell_box", "[Loader]") {
|
||||
|
||||
@@ -319,10 +319,107 @@ 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(
|
||||
|
||||
@@ -24,33 +24,6 @@ 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
|
||||
//
|
||||
|
||||
//
|
||||
@@ -62,9 +35,9 @@ THE SOFTWARE.
|
||||
#ifndef TINY_OBJ_LOADER_H_
|
||||
#define TINY_OBJ_LOADER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace tinyobj {
|
||||
|
||||
@@ -91,6 +64,22 @@ 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;
|
||||
|
||||
@@ -112,10 +101,11 @@ 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 {
|
||||
@@ -131,14 +121,22 @@ typedef struct {
|
||||
} attrib_t;
|
||||
|
||||
typedef struct callback_t_ {
|
||||
void (*vertex_cb)(void *user_data, float x, float y, float z);
|
||||
// W is optional and set to 1 if there is no `w` item in `v` line
|
||||
void (*vertex_cb)(void *user_data, float x, float y, float z, float w);
|
||||
void (*normal_cb)(void *user_data, float x, float y, float z);
|
||||
void (*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
|
||||
|
||||
// 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
|
||||
// a material not found in .mtl
|
||||
void (*usemtl_cb)(void *user_data, const char *name, int materialId);
|
||||
void (*usemtl_cb)(void *user_data, const char *name, int material_id);
|
||||
// `materials` = parsed material data.
|
||||
void (*mtllib_cb)(void *user_data, const material_t *materials,
|
||||
int num_materials);
|
||||
@@ -155,7 +153,6 @@ typedef struct callback_t_ {
|
||||
mtllib_cb(NULL),
|
||||
group_cb(NULL),
|
||||
object_cb(NULL) {}
|
||||
|
||||
} callback_t;
|
||||
|
||||
class MaterialReader {
|
||||
@@ -200,12 +197,11 @@ 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`
|
||||
/// '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);
|
||||
/// 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);
|
||||
|
||||
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
|
||||
/// std::istream for materials.
|
||||
@@ -223,12 +219,12 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
||||
} // namespace tinyobj
|
||||
|
||||
#ifdef TINYOBJLOADER_IMPLEMENTATION
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include <fstream>
|
||||
@@ -261,6 +257,38 @@ 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))
|
||||
@@ -424,18 +452,13 @@ fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline float parseFloat(const char **token) {
|
||||
static inline float parseFloat(const char **token, double default_value = 0.0) {
|
||||
(*token) += strspn((*token), " \t");
|
||||
#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 = 0.0;
|
||||
double val = default_value;
|
||||
tryParseDouble((*token), end, &val);
|
||||
float f = static_cast<float>(val);
|
||||
(*token) = end;
|
||||
#endif
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -451,6 +474,14 @@ 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;
|
||||
|
||||
@@ -510,8 +541,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize,
|
||||
|
||||
// Parse raw triples: i, i/j/k, i//k, i/j
|
||||
static vertex_index parseRawTriple(const char **token) {
|
||||
vertex_index vi(
|
||||
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
|
||||
vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
|
||||
|
||||
vi.v_idx = atoi((*token));
|
||||
(*token) += strcspn((*token), "/ \t\r");
|
||||
@@ -562,6 +592,20 @@ 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();
|
||||
}
|
||||
|
||||
@@ -616,7 +660,8 @@ 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
|
||||
}
|
||||
}
|
||||
@@ -633,12 +678,15 @@ 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) {
|
||||
inStream->getline(&buf[0], static_cast<std::streamsize>(maxchars));
|
||||
std::string linebuf;
|
||||
|
||||
std::string linebuf(&buf[0]);
|
||||
safeGetline(*inStream, linebuf);
|
||||
|
||||
// Trim trailing whitespace.
|
||||
if (linebuf.size() > 0) {
|
||||
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
|
||||
}
|
||||
|
||||
// Trim newline '\r\n' or '\n'
|
||||
if (linebuf.size() > 0) {
|
||||
@@ -722,7 +770,8 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
||||
}
|
||||
|
||||
// transmittance
|
||||
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
|
||||
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
|
||||
token += 2;
|
||||
float r, g, b;
|
||||
parseFloat3(&r, &g, &b, &token);
|
||||
@@ -777,6 +826,55 @@ 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;
|
||||
@@ -833,6 +931,41 @@ 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) {
|
||||
@@ -926,12 +1059,9 @@ 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) {
|
||||
inStream->getline(&buf[0], maxchars);
|
||||
|
||||
std::string linebuf(&buf[0]);
|
||||
std::string linebuf;
|
||||
safeGetline(*inStream, linebuf);
|
||||
|
||||
// Trim newline '\r\n' or '\n'
|
||||
if (linebuf.size() > 0) {
|
||||
@@ -1193,21 +1323,26 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
std::string *err, std::istream *inStream,
|
||||
MaterialReader *readMatFn) {
|
||||
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
|
||||
void *user_data /*= NULL*/,
|
||||
MaterialReader *readMatFn /*= NULL*/,
|
||||
std::string *err /*= NULL*/) {
|
||||
std::stringstream errss;
|
||||
|
||||
// material
|
||||
std::map<std::string, int> material_map;
|
||||
int materialId = -1; // -1 = invalid
|
||||
int material_id = -1; // -1 = invalid
|
||||
|
||||
int maxchars = 8192; // Alloc enough size.
|
||||
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
|
||||
while (inStream->peek() != -1) {
|
||||
inStream->getline(&buf[0], maxchars);
|
||||
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;
|
||||
|
||||
std::string linebuf(&buf[0]);
|
||||
std::string linebuf;
|
||||
while (inStream.peek() != -1) {
|
||||
safeGetline(inStream, linebuf);
|
||||
|
||||
// Trim newline '\r\n' or '\n'
|
||||
if (linebuf.size() > 0) {
|
||||
@@ -1236,10 +1371,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
// vertex
|
||||
if (token[0] == 'v' && IS_SPACE((token[1]))) {
|
||||
token += 2;
|
||||
float x, y, z;
|
||||
parseFloat3(&x, &y, &z, &token);
|
||||
float x, y, z, w; // w is optional. default = 1.0
|
||||
parseV(&x, &y, &z, &w, &token);
|
||||
if (callback.vertex_cb) {
|
||||
callback.vertex_cb(user_data, x, y, z);
|
||||
callback.vertex_cb(user_data, x, y, z, w);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1258,10 +1393,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
// texcoord
|
||||
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||
token += 3;
|
||||
float x, y;
|
||||
parseFloat2(&x, &y, &token);
|
||||
float x, y, z; // y and z are optional. default = 0.0
|
||||
parseFloat3(&x, &y, &z, &token);
|
||||
if (callback.texcoord_cb) {
|
||||
callback.texcoord_cb(user_data, x, y);
|
||||
callback.texcoord_cb(user_data, x, y, z);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1271,15 +1406,25 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
token += 2;
|
||||
token += strspn(token, " \t");
|
||||
|
||||
indices.clear();
|
||||
while (!IS_NEW_LINE(token[0])) {
|
||||
vertex_index vi = parseRawTriple(&token);
|
||||
if (callback.index_cb) {
|
||||
callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx);
|
||||
}
|
||||
|
||||
index_t idx;
|
||||
idx.vertex_index = vi.v_idx;
|
||||
idx.normal_index = vi.vn_idx;
|
||||
idx.texcoord_index = vi.vt_idx;
|
||||
|
||||
indices.push_back(idx);
|
||||
size_t n = strspn(token, " \t\r");
|
||||
token += n;
|
||||
}
|
||||
|
||||
if (callback.index_cb && indices.size() > 0) {
|
||||
callback.index_cb(user_data, &indices.at(0),
|
||||
static_cast<int>(indices.size()));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1288,7 +1433,8 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
|
||||
token += 7;
|
||||
#ifdef _MSC_VER
|
||||
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
|
||||
sscanf_s(token, "%s", namebuf,
|
||||
static_cast<unsigned int>(_countof(namebuf)));
|
||||
#else
|
||||
sscanf(token, "%s", namebuf);
|
||||
#endif
|
||||
@@ -1300,12 +1446,12 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
// { error!! material not found }
|
||||
}
|
||||
|
||||
if (newMaterialId != materialId) {
|
||||
materialId = newMaterialId;
|
||||
if (newMaterialId != material_id) {
|
||||
material_id = newMaterialId;
|
||||
}
|
||||
|
||||
if (callback.usemtl_cb) {
|
||||
callback.usemtl_cb(user_data, namebuf, materialId);
|
||||
callback.usemtl_cb(user_data, namebuf, material_id);
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -1313,28 +1459,30 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
|
||||
// load mtl
|
||||
if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
|
||||
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
|
||||
token += 7;
|
||||
if (readMatFn) {
|
||||
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;
|
||||
std::vector<material_t> materials;
|
||||
bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
|
||||
if (err) {
|
||||
(*err) += err_mtl;
|
||||
}
|
||||
std::string err_mtl;
|
||||
materials.clear();
|
||||
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;
|
||||
@@ -1342,8 +1490,7 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
||||
|
||||
// group name
|
||||
if (token[0] == 'g' && IS_SPACE((token[1]))) {
|
||||
std::vector<std::string> names;
|
||||
names.reserve(2);
|
||||
names.clear();
|
||||
|
||||
while (!IS_NEW_LINE(token[0])) {
|
||||
std::string str = parseString(&token);
|
||||
@@ -1353,51 +1500,49 @@ bool LoadObjWithCallback(void *user_data, 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 = "";
|
||||
name.clear();
|
||||
}
|
||||
|
||||
if (callback.group_cb) {
|
||||
if (names.size() > 1) {
|
||||
// create const char* array.
|
||||
std::vector<const char *> tmp(names.size() - 1);
|
||||
for (size_t j = 0; j < tmp.size(); j++) {
|
||||
tmp[j] = names[j + 1].c_str();
|
||||
names_out.resize(names.size() - 1);
|
||||
for (size_t j = 0; j < names_out.size(); j++) {
|
||||
names_out[j] = names[j + 1].c_str();
|
||||
}
|
||||
callback.group_cb(user_data, &tmp.at(0),
|
||||
static_cast<int>(tmp.size()));
|
||||
callback.group_cb(user_data, &names_out.at(0),
|
||||
static_cast<int>(names_out.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 name = std::string(namebuf);
|
||||
std::string object_name = std::string(namebuf);
|
||||
|
||||
if (callback.object_cb) {
|
||||
callback.object_cb(user_data, name.c_str());
|
||||
}
|
||||
if (callback.object_cb) {
|
||||
callback.object_cb(user_data, object_name.c_str());
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 0 // @todo
|
||||
if (token[0] == 't' && IS_SPACE(token[1])) {
|
||||
|
||||
Reference in New Issue
Block a user