Compare commits
65 Commits
| 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)
|
[](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
@@ -17,29 +16,24 @@ Tiny but powerful single file wavefront obj loader written in C++. No dependency
|
|||||||
|
|
||||||
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
|
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
|
||||||
|
|
||||||
|
If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c .
|
||||||
|
|
||||||
What's new
|
Notice!
|
||||||
----------
|
|
||||||
|
|
||||||
* XX YY, ZZZZ : New data strcutre and API!
|
|
||||||
* Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert!
|
|
||||||
* Nov 26, 2015 : Now single-header only!.
|
|
||||||
* Nov 08, 2015 : Improved API.
|
|
||||||
* Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors!
|
|
||||||
* Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel!
|
|
||||||
* Feb 06, 2015 : Fix parsing multi-material object
|
|
||||||
* Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo!
|
|
||||||
* Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran!
|
|
||||||
* Apr 29, 2014 : Add API to read .obj from std::istream. Good for reading compressed .obj or connecting to procedural primitive generator. Thanks burnse!
|
|
||||||
* Apr 21, 2014 : Define default material if no material definition exists in .obj. Thanks YarmUI!
|
|
||||||
* Apr 10, 2014 : Add support for parsing 'illum' and 'd'/'Tr' statements. Thanks mmp!
|
|
||||||
* Jan 27, 2014 : Added CMake project. Thanks bradc6!
|
|
||||||
* Nov 26, 2013 : Performance optimization by NeuralSandwich. 9% improvement in his project, thanks!
|
|
||||||
* Sep 12, 2013 : Added multiple .obj sticher example.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
We have released new version v1.0.0 on 20 Aug, 2016.
|
||||||
|
Old version is available `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x
|
||||||
|
|
||||||
|
## What's new
|
||||||
|
|
||||||
|
* 20 Aug, 2016 : Bump version v1.0.0. New data strcutre and API!
|
||||||
|
|
||||||
|
### Old version
|
||||||
|
|
||||||
|
Previous old version is avaiable in `v0.9.x` branch.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||

|

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

|

|
||||||
|
|
||||||
* [examples/viewer/](examples/viewer) OpenGL .obj viewer
|
* [examples/viewer/](examples/viewer) OpenGL .obj viewer
|
||||||
|
* [examples/callback_api/](examples/callback_api/) Callback API example
|
||||||
|
* [examples/voxelize/](examples/voxelize/) Voxelizer example
|
||||||
|
|
||||||
Use case
|
## Use case
|
||||||
--------
|
|
||||||
|
|
||||||
TinyObjLoader is successfully used in ...
|
TinyObjLoader is successfully used in ...
|
||||||
|
|
||||||
|
### New version(v1.0.x)
|
||||||
|
|
||||||
|
* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models
|
||||||
|
* Your project here!
|
||||||
|
|
||||||
|
### Old version(v0.9.x)
|
||||||
|
|
||||||
* bullet3 https://github.com/erwincoumans/bullet3
|
* bullet3 https://github.com/erwincoumans/bullet3
|
||||||
* pbrt-v2 https://github.com/mmp/pbrt-v2
|
* pbrt-v2 https://github.com/mmp/pbrt-v2
|
||||||
* OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01
|
* OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01
|
||||||
@@ -65,10 +67,14 @@ TinyObjLoader is successfully used in ...
|
|||||||
* pbrt-v3 https://github.com/mmp/pbrt-v3
|
* pbrt-v3 https://github.com/mmp/pbrt-v3
|
||||||
* cocos2d-x https://github.com/cocos2d/cocos2d-x/
|
* cocos2d-x https://github.com/cocos2d/cocos2d-x/
|
||||||
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan
|
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan
|
||||||
* Your project here!
|
* voxelizer https://github.com/karimnaaji/voxelizer
|
||||||
|
* Probulator https://github.com/kayru/Probulator
|
||||||
|
* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking
|
||||||
|
* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK
|
||||||
|
* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg
|
||||||
|
* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl
|
||||||
|
|
||||||
Features
|
## Features
|
||||||
--------
|
|
||||||
|
|
||||||
* Group(parse multiple group name)
|
* Group(parse multiple group name)
|
||||||
* Vertex
|
* Vertex
|
||||||
@@ -80,20 +86,16 @@ Features
|
|||||||
* Callback API for custom loading.
|
* Callback API for custom loading.
|
||||||
|
|
||||||
|
|
||||||
TODO
|
## TODO
|
||||||
----
|
|
||||||
|
|
||||||
* [ ] Fix Python binding.
|
|
||||||
* [ ] Fix obj_sticker example.
|
* [ ] Fix obj_sticker example.
|
||||||
* [ ] More unit test codes.
|
* [ ] More unit test codes.
|
||||||
|
|
||||||
License
|
## License
|
||||||
-------
|
|
||||||
|
|
||||||
Licensed under MIT license.
|
Licensed under MIT license.
|
||||||
|
|
||||||
Usage
|
## Usage
|
||||||
-----
|
|
||||||
|
|
||||||
`attrib_t` contains single and linear array of vertex data(position, normal and texcoord).
|
`attrib_t` contains single and linear array of vertex data(position, normal and texcoord).
|
||||||
Each `shape_t` does not contain vertex data but contains array index to `attrib_t`.
|
Each `shape_t` does not contain vertex data but contains array index to `attrib_t`.
|
||||||
@@ -148,8 +150,7 @@ for (size_t s = 0; s < shapes.size(); s++) {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Optimized loader
|
## Optimized loader
|
||||||
----------------
|
|
||||||
|
|
||||||
Optimized multi-threaded .obj loader is available at `experimental/` directory.
|
Optimized multi-threaded .obj loader is available at `experimental/` directory.
|
||||||
If you want absolute performance to load .obj data, this optimized loader will fit your purpose.
|
If you want absolute performance to load .obj data, this optimized loader will fit your purpose.
|
||||||
@@ -163,7 +164,6 @@ Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core
|
|||||||
* optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline)
|
* optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline)
|
||||||
|
|
||||||
|
|
||||||
Tests
|
## Tests
|
||||||
-----
|
|
||||||
|
|
||||||
Unit tests are provided in `tests` directory. See `tests/README.md` for details.
|
Unit tests are provided in `tests` directory. See `tests/README.md` for details.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: 0.9.{build}
|
version: 1.0.{build}
|
||||||
|
|
||||||
platform: x64
|
platform: x64
|
||||||
|
|
||||||
@@ -7,7 +7,6 @@ install:
|
|||||||
# All external dependencies are installed in C:\projects\deps
|
# All external dependencies are installed in C:\projects\deps
|
||||||
#######################################################################################
|
#######################################################################################
|
||||||
- mkdir C:\projects\deps
|
- mkdir C:\projects\deps
|
||||||
- cd C:\projects\deps
|
|
||||||
|
|
||||||
#######################################################################################
|
#######################################################################################
|
||||||
# Install Ninja
|
# Install Ninja
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
|
//
|
||||||
|
// An example of how to use callback API.
|
||||||
|
// This example is minimum and incomplete. Just showing the usage of callback
|
||||||
|
// API.
|
||||||
|
// You need to implement your own Mesh data struct constrution based on this
|
||||||
|
// example in practical.
|
||||||
|
//
|
||||||
#define TINYOBJLOADER_IMPLEMENTATION
|
#define TINYOBJLOADER_IMPLEMENTATION
|
||||||
#include "tiny_obj_loader.h"
|
#include "tiny_obj_loader.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cassert>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
std::vector<float> vertices;
|
std::vector<float> vertices;
|
||||||
std::vector<float> normals;
|
std::vector<float> normals;
|
||||||
std::vector<float> texcoords;
|
std::vector<float> texcoords;
|
||||||
@@ -21,18 +27,17 @@ typedef struct
|
|||||||
|
|
||||||
} MyMesh;
|
} MyMesh;
|
||||||
|
|
||||||
void vertex_cb(void *user_data, float x, float y, float z)
|
void vertex_cb(void *user_data, float x, float y, float z, float w) {
|
||||||
{
|
|
||||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||||
printf("v[%ld] = %f, %f, %f\n", mesh->vertices.size() / 3, x, y, z);
|
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(x);
|
||||||
mesh->vertices.push_back(y);
|
mesh->vertices.push_back(y);
|
||||||
mesh->vertices.push_back(z);
|
mesh->vertices.push_back(z);
|
||||||
|
// Discard w
|
||||||
}
|
}
|
||||||
|
|
||||||
void normal_cb(void *user_data, float x, float y, float z)
|
void normal_cb(void *user_data, float x, float y, float z) {
|
||||||
{
|
|
||||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||||
printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z);
|
printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z);
|
||||||
|
|
||||||
@@ -41,48 +46,55 @@ void normal_cb(void *user_data, float x, float y, float z)
|
|||||||
mesh->normals.push_back(z);
|
mesh->normals.push_back(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void texcoord_cb(void *user_data, float x, float y)
|
void texcoord_cb(void *user_data, float x, float y, float z) {
|
||||||
{
|
|
||||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||||
printf("vt[%ld] = %f, %f\n", mesh->texcoords.size() / 2, x, y);
|
printf("vt[%ld] = %f, %f, %f\n", mesh->texcoords.size() / 3, x, y, z);
|
||||||
|
|
||||||
mesh->texcoords.push_back(x);
|
mesh->texcoords.push_back(x);
|
||||||
mesh->texcoords.push_back(y);
|
mesh->texcoords.push_back(y);
|
||||||
|
mesh->texcoords.push_back(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx)
|
void index_cb(void *user_data, tinyobj::index_t *indices, int num_indices) {
|
||||||
{
|
|
||||||
// NOTE: the value of each index is raw value.
|
// NOTE: the value of each index is raw value.
|
||||||
// For example, the application must manually adjust the index with offset
|
// For example, the application must manually adjust the index with offset
|
||||||
// (e.g. v_indices.size()) when the value is negative(relative index).
|
// (e.g. v_indices.size()) when the value is negative(whic means relative
|
||||||
|
// index).
|
||||||
|
// Also, the first index starts with 1, not 0.
|
||||||
// See fixIndex() function in tiny_obj_loader.h for details.
|
// See fixIndex() function in tiny_obj_loader.h for details.
|
||||||
// Also, -2147483648(0x80000000) is set for the index value which does not exist in .obj
|
// Also, 0 is set for the index value which
|
||||||
|
// does not exist in .obj
|
||||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||||
printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), v_idx, vn_idx, vt_idx);
|
|
||||||
|
|
||||||
if (v_idx != 0x80000000) {
|
for (int i = 0; i < num_indices; i++) {
|
||||||
mesh->v_indices.push_back(v_idx);
|
tinyobj::index_t idx = indices[i];
|
||||||
|
printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), idx.vertex_index,
|
||||||
|
idx.normal_index, idx.texcoord_index);
|
||||||
|
|
||||||
|
if (idx.vertex_index != 0) {
|
||||||
|
mesh->v_indices.push_back(idx.vertex_index);
|
||||||
}
|
}
|
||||||
if (vn_idx != 0x80000000) {
|
if (idx.normal_index != 0) {
|
||||||
mesh->vn_indices.push_back(vn_idx);
|
mesh->vn_indices.push_back(idx.normal_index);
|
||||||
|
}
|
||||||
|
if (idx.texcoord_index != 0) {
|
||||||
|
mesh->vt_indices.push_back(idx.texcoord_index);
|
||||||
}
|
}
|
||||||
if (vt_idx != 0x80000000) {
|
|
||||||
mesh->vt_indices.push_back(vt_idx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usemtl_cb(void *user_data, const char* name, int material_idx)
|
void usemtl_cb(void *user_data, const char *name, int material_idx) {
|
||||||
{
|
|
||||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||||
if ((material_idx > -1) && (material_idx < mesh->materials.size())) {
|
if ((material_idx > -1) && (material_idx < mesh->materials.size())) {
|
||||||
printf("usemtl. material id = %d(name = %s)\n", material_idx, mesh->materials[material_idx].name.c_str());
|
printf("usemtl. material id = %d(name = %s)\n", material_idx,
|
||||||
|
mesh->materials[material_idx].name.c_str());
|
||||||
} else {
|
} else {
|
||||||
printf("usemtl. name = %s\n", name);
|
printf("usemtl. name = %s\n", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_materials)
|
void mtllib_cb(void *user_data, const tinyobj::material_t *materials,
|
||||||
{
|
int num_materials) {
|
||||||
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data);
|
||||||
printf("mtllib. # of materials = %d\n", num_materials);
|
printf("mtllib. # of materials = %d\n", num_materials);
|
||||||
|
|
||||||
@@ -91,8 +103,7 @@ void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_ma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void group_cb(void *user_data, const char **names, int num_names)
|
void group_cb(void *user_data, const char **names, int num_names) {
|
||||||
{
|
|
||||||
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||||
printf("group : name = \n");
|
printf("group : name = \n");
|
||||||
|
|
||||||
@@ -101,16 +112,12 @@ void group_cb(void *user_data, const char **names, int num_names)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void object_cb(void *user_data, const char *name)
|
void object_cb(void *user_data, const char *name) {
|
||||||
{
|
|
||||||
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
|
||||||
printf("object : name = %s\n", name);
|
printf("object : name = %s\n", name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int main(int argc, char **argv) {
|
||||||
main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
tinyobj::callback_t cb;
|
tinyobj::callback_t cb;
|
||||||
cb.vertex_cb = vertex_cb;
|
cb.vertex_cb = vertex_cb;
|
||||||
cb.normal_cb = normal_cb;
|
cb.normal_cb = normal_cb;
|
||||||
@@ -123,7 +130,11 @@ main(int argc, char** argv)
|
|||||||
|
|
||||||
MyMesh mesh;
|
MyMesh mesh;
|
||||||
std::string err;
|
std::string err;
|
||||||
std::ifstream ifs("../../models/cornell_box.obj");
|
std::string filename = "../../models/cornell_box.obj";
|
||||||
|
if (argc > 1) {
|
||||||
|
filename = std::string(argv[1]);
|
||||||
|
}
|
||||||
|
std::ifstream ifs(filename.c_str());
|
||||||
|
|
||||||
if (ifs.fail()) {
|
if (ifs.fail()) {
|
||||||
std::cerr << "file not found." << std::endl;
|
std::cerr << "file not found." << std::endl;
|
||||||
@@ -132,7 +143,7 @@ main(int argc, char** argv)
|
|||||||
|
|
||||||
tinyobj::MaterialFileReader mtlReader("../../models/");
|
tinyobj::MaterialFileReader mtlReader("../../models/");
|
||||||
|
|
||||||
bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader);
|
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err);
|
||||||
|
|
||||||
if (!err.empty()) {
|
if (!err.empty()) {
|
||||||
std::cerr << err << std::endl;
|
std::cerr << err << std::endl;
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
//
|
//
|
||||||
// Simple .obj viewer(vertex only)
|
// Simple .obj viewer(vertex only)
|
||||||
//
|
//
|
||||||
#include <vector>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cmath>
|
#include <string>
|
||||||
#include <cassert>
|
#include <vector>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
#include <windows.h>
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -161,8 +161,9 @@ void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& drawObjects, const char* filename)
|
bool LoadObjAndConvert(float bmin[3], float bmax[3],
|
||||||
{
|
std::vector<DrawObject>* drawObjects,
|
||||||
|
const char* filename) {
|
||||||
tinyobj::attrib_t attrib;
|
tinyobj::attrib_t attrib;
|
||||||
std::vector<tinyobj::shape_t> shapes;
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
@@ -172,7 +173,8 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
tm.start();
|
tm.start();
|
||||||
|
|
||||||
std::string err;
|
std::string err;
|
||||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
|
bool ret =
|
||||||
|
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
|
||||||
if (!err.empty()) {
|
if (!err.empty()) {
|
||||||
std::cerr << err << std::endl;
|
std::cerr << err << std::endl;
|
||||||
}
|
}
|
||||||
@@ -184,7 +186,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Parsing time: %d [ms]\n", tm.msec());
|
printf("Parsing time: %d [ms]\n", (int)tm.msec());
|
||||||
|
|
||||||
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
|
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
|
||||||
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
|
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
|
||||||
@@ -200,7 +202,6 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
DrawObject o;
|
DrawObject o;
|
||||||
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
||||||
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) {
|
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) {
|
||||||
|
|
||||||
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
|
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
|
||||||
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
|
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 idx2 = shapes[s].mesh.indices[3 * f + 2];
|
||||||
@@ -242,8 +243,12 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
} else {
|
} else {
|
||||||
// compute geometric normal
|
// compute geometric normal
|
||||||
CalcNormal(n[0], v[0], v[1], v[2]);
|
CalcNormal(n[0], v[0], v[1], v[2]);
|
||||||
n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2];
|
n[1][0] = n[0][0];
|
||||||
n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2];
|
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++) {
|
for (int k = 0; k < 3; k++) {
|
||||||
@@ -267,7 +272,6 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
vb.push_back(c[1] * 0.5 + 0.5);
|
vb.push_back(c[1] * 0.5 + 0.5);
|
||||||
vb.push_back(c[2] * 0.5 + 0.5);
|
vb.push_back(c[2] * 0.5 + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
o.vb = 0;
|
o.vb = 0;
|
||||||
@@ -275,12 +279,14 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
if (vb.size() > 0) {
|
if (vb.size() > 0) {
|
||||||
glGenBuffers(1, &o.vb);
|
glGenBuffers(1, &o.vb);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
|
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
|
||||||
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0),
|
||||||
|
GL_STATIC_DRAW);
|
||||||
o.numTriangles = vb.size() / 9 / 3;
|
o.numTriangles = vb.size() / 9 / 3;
|
||||||
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles);
|
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
|
||||||
|
o.numTriangles);
|
||||||
}
|
}
|
||||||
|
|
||||||
gDrawObjects.push_back(o);
|
drawObjects->push_back(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,8 +296,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& dr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reshapeFunc(GLFWwindow* window, int w, int h)
|
void reshapeFunc(GLFWwindow* window, int w, int h) {
|
||||||
{
|
|
||||||
int fb_w, fb_h;
|
int fb_w, fb_h;
|
||||||
// Get actual framebuffer size.
|
// Get actual framebuffer size.
|
||||||
glfwGetFramebufferSize(window, &fb_w, &fb_h);
|
glfwGetFramebufferSize(window, &fb_w, &fb_h);
|
||||||
@@ -307,25 +312,38 @@ void reshapeFunc(GLFWwindow* window, int w, int h)
|
|||||||
height = h;
|
height = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) {
|
void keyboardFunc(GLFWwindow* window, int key, int scancode, int action,
|
||||||
|
int mods) {
|
||||||
|
(void)window;
|
||||||
|
(void)scancode;
|
||||||
|
(void)mods;
|
||||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||||
// Move camera
|
// Move camera
|
||||||
float mv_x = 0, mv_y = 0, mv_z = 0;
|
float mv_x = 0, mv_y = 0, mv_z = 0;
|
||||||
if(key == GLFW_KEY_K) mv_x += 1;
|
if (key == GLFW_KEY_K)
|
||||||
else if(key == GLFW_KEY_J) mv_x += -1;
|
mv_x += 1;
|
||||||
else if(key == GLFW_KEY_L) mv_y += 1;
|
else if (key == GLFW_KEY_J)
|
||||||
else if(key == GLFW_KEY_H) mv_y += -1;
|
mv_x += -1;
|
||||||
else if(key == GLFW_KEY_P) mv_z += 1;
|
else if (key == GLFW_KEY_L)
|
||||||
else if(key == GLFW_KEY_N) mv_z += -1;
|
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);
|
// camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
|
||||||
// Close window
|
// Close window
|
||||||
if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE);
|
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE)
|
||||||
|
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||||
|
|
||||||
// init_frame = true;
|
// init_frame = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clickFunc(GLFWwindow* window, int button, int action, int mods) {
|
void clickFunc(GLFWwindow* window, int button, int action, int mods) {
|
||||||
|
(void)window;
|
||||||
|
(void)mods;
|
||||||
if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
||||||
if (action == GLFW_PRESS) {
|
if (action == GLFW_PRESS) {
|
||||||
mouseLeftPressed = true;
|
mouseLeftPressed = true;
|
||||||
@@ -351,12 +369,12 @@ void clickFunc(GLFWwindow* window, int button, int action, int mods){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
|
void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) {
|
||||||
|
(void)window;
|
||||||
float rotScale = 1.0f;
|
float rotScale = 1.0f;
|
||||||
float transScale = 2.0f;
|
float transScale = 2.0f;
|
||||||
|
|
||||||
if (mouseLeftPressed) {
|
if (mouseLeftPressed) {
|
||||||
trackball(prev_quat,
|
trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width,
|
||||||
rotScale * (2.0f * prevMouseX - width) / (float)width,
|
|
||||||
rotScale * (height - 2.0f * prevMouseY) / (float)height,
|
rotScale * (height - 2.0f * prevMouseY) / (float)height,
|
||||||
rotScale * (2.0f * mouse_x - width) / (float)width,
|
rotScale * (2.0f * mouse_x - width) / (float)width,
|
||||||
rotScale * (height - 2.0f * mouse_y) / (float)height);
|
rotScale * (height - 2.0f * mouse_y) / (float)height);
|
||||||
@@ -377,8 +395,7 @@ void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){
|
|||||||
prevMouseY = mouse_y;
|
prevMouseY = mouse_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Draw(const std::vector<DrawObject>& drawObjects)
|
void Draw(const std::vector<DrawObject>& drawObjects) {
|
||||||
{
|
|
||||||
glPolygonMode(GL_FRONT, GL_FILL);
|
glPolygonMode(GL_FRONT, GL_FILL);
|
||||||
glPolygonMode(GL_BACK, GL_FILL);
|
glPolygonMode(GL_BACK, GL_FILL);
|
||||||
|
|
||||||
@@ -443,9 +460,7 @@ static void Init() {
|
|||||||
up[2] = 0.0f;
|
up[2] = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
std::cout << "Needs input.obj\n" << std::endl;
|
std::cout << "Needs input.obj\n" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -453,14 +468,11 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
Init();
|
Init();
|
||||||
|
|
||||||
|
|
||||||
if (!glfwInit()) {
|
if (!glfwInit()) {
|
||||||
std::cerr << "Failed to initialize GLFW." << std::endl;
|
std::cerr << "Failed to initialize GLFW." << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
|
window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
|
||||||
if (window == NULL) {
|
if (window == NULL) {
|
||||||
std::cerr << "Failed to open GLFW window. " << std::endl;
|
std::cerr << "Failed to open GLFW window. " << std::endl;
|
||||||
@@ -486,7 +498,7 @@ int main(int argc, char **argv)
|
|||||||
reshapeFunc(window, width, height);
|
reshapeFunc(window, width, height);
|
||||||
|
|
||||||
float bmin[3], bmax[3];
|
float bmin[3], bmax[3];
|
||||||
if (false == LoadObjAndConvert(bmin, bmax, gDrawObjects, argv[1])) {
|
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, argv[1])) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +521,8 @@ int main(int argc, char **argv)
|
|||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
GLfloat mat[4][4];
|
GLfloat mat[4][4];
|
||||||
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]);
|
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
|
||||||
|
up[1], up[2]);
|
||||||
build_rotmatrix(mat, curr_quat);
|
build_rotmatrix(mat, curr_quat);
|
||||||
glMultMatrixf(&mat[0][0]);
|
glMultMatrixf(&mat[0][0]);
|
||||||
|
|
||||||
@@ -517,7 +530,8 @@ int main(int argc, char **argv)
|
|||||||
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
|
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
|
||||||
|
|
||||||
// Centerize object.
|
// Centerize object.
|
||||||
glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2]));
|
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]),
|
||||||
|
-0.5 * (bmax[2] + bmin[2]));
|
||||||
|
|
||||||
Draw(gDrawObjects);
|
Draw(gDrawObjects);
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,22 @@ typedef struct {
|
|||||||
std::string bump_texname; // map_bump, bump
|
std::string bump_texname; // map_bump, bump
|
||||||
std::string displacement_texname; // disp
|
std::string displacement_texname; // disp
|
||||||
std::string alpha_texname; // map_d
|
std::string alpha_texname; // map_d
|
||||||
|
|
||||||
|
// PBR extension
|
||||||
|
// http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
|
||||||
|
float roughness; // [0, 1] default 0
|
||||||
|
float metallic; // [0, 1] default 0
|
||||||
|
float sheen; // [0, 1] default 0
|
||||||
|
float clearcoat_thickness; // [0, 1] default 0
|
||||||
|
float clearcoat_roughness; // [0, 1] default 0
|
||||||
|
float anisotropy; // aniso. [0, 1] default 0
|
||||||
|
float anisotropy_rotation; // anisor. [0, 1] default 0
|
||||||
|
std::string roughness_texname; // map_Pr
|
||||||
|
std::string metallic_texname; // map_Pm
|
||||||
|
std::string sheen_texname; // map_Ps
|
||||||
|
std::string emissive_texname; // map_Ke
|
||||||
|
std::string normal_texname; // norm. For normal mapping.
|
||||||
|
|
||||||
std::map<std::string, std::string> unknown_parameter;
|
std::map<std::string, std::string> unknown_parameter;
|
||||||
} material_t;
|
} material_t;
|
||||||
|
|
||||||
@@ -301,19 +317,20 @@ typedef struct {
|
|||||||
unsigned int length;
|
unsigned int length;
|
||||||
} shape_t;
|
} shape_t;
|
||||||
|
|
||||||
struct vertex_index {
|
struct index_t {
|
||||||
int v_idx, vt_idx, vn_idx;
|
int vertex_index, texcoord_index, normal_index;
|
||||||
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
|
index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {}
|
||||||
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
|
explicit index_t(int idx)
|
||||||
vertex_index(int vidx, int vtidx, int vnidx)
|
: vertex_index(idx), texcoord_index(idx), normal_index(idx) {}
|
||||||
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
|
index_t(int vidx, int vtidx, int vnidx)
|
||||||
|
: vertex_index(vidx), texcoord_index(vtidx), normal_index(vnidx) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
std::vector<float, lt::allocator<float> > vertices;
|
std::vector<float, lt::allocator<float> > vertices;
|
||||||
std::vector<float, lt::allocator<float> > normals;
|
std::vector<float, lt::allocator<float> > normals;
|
||||||
std::vector<float, lt::allocator<float> > texcoords;
|
std::vector<float, lt::allocator<float> > texcoords;
|
||||||
std::vector<vertex_index, lt::allocator<vertex_index> > faces;
|
std::vector<index_t, lt::allocator<index_t> > indices;
|
||||||
std::vector<int, lt::allocator<int> > face_num_verts;
|
std::vector<int, lt::allocator<int> > face_num_verts;
|
||||||
std::vector<int, lt::allocator<int> > material_ids;
|
std::vector<int, lt::allocator<int> > material_ids;
|
||||||
} attrib_t;
|
} attrib_t;
|
||||||
@@ -386,12 +403,11 @@ static inline int fixIndex(int idx, int n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse raw triples: i, i/j/k, i//k, i/j
|
// Parse raw triples: i, i/j/k, i//k, i/j
|
||||||
static vertex_index parseRawTriple(const char **token) {
|
static index_t parseRawTriple(const char **token) {
|
||||||
vertex_index vi(
|
index_t vi(
|
||||||
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
|
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
|
||||||
|
|
||||||
vi.v_idx = my_atoi((*token));
|
vi.vertex_index = my_atoi((*token));
|
||||||
//(*token) += strcspn((*token), "/ \t\r");
|
|
||||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||||
(*token)++;
|
(*token)++;
|
||||||
@@ -404,8 +420,7 @@ static vertex_index parseRawTriple(const char **token) {
|
|||||||
// i//k
|
// i//k
|
||||||
if ((*token)[0] == '/') {
|
if ((*token)[0] == '/') {
|
||||||
(*token)++;
|
(*token)++;
|
||||||
vi.vn_idx = my_atoi((*token));
|
vi.normal_index = my_atoi((*token));
|
||||||
//(*token) += strcspn((*token), "/ \t\r");
|
|
||||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||||
(*token)++;
|
(*token)++;
|
||||||
@@ -414,8 +429,7 @@ static vertex_index parseRawTriple(const char **token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// i/j/k or i/j
|
// i/j/k or i/j
|
||||||
vi.vt_idx = my_atoi((*token));
|
vi.texcoord_index = my_atoi((*token));
|
||||||
//(*token) += strcspn((*token), "/ \t\r");
|
|
||||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||||
(*token)++;
|
(*token)++;
|
||||||
@@ -426,8 +440,7 @@ static vertex_index parseRawTriple(const char **token) {
|
|||||||
|
|
||||||
// i/j/k
|
// i/j/k
|
||||||
(*token)++; // skip '/'
|
(*token)++; // skip '/'
|
||||||
vi.vn_idx = my_atoi((*token));
|
vi.normal_index = my_atoi((*token));
|
||||||
//(*token) += strcspn((*token), "/ \t\r");
|
|
||||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||||
(*token)++;
|
(*token)++;
|
||||||
@@ -436,17 +449,17 @@ static vertex_index parseRawTriple(const char **token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool parseString(ShortString *s, const char **token) {
|
static inline bool parseString(ShortString *s, const char **token) {
|
||||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
skip_space(token);
|
||||||
size_t e = until_space((*token)); // strcspn((*token), " \t\r");
|
size_t e = until_space((*token));
|
||||||
(*s)->insert((*s)->end(), (*token), (*token) + e);
|
(*s)->insert((*s)->end(), (*token), (*token) + e);
|
||||||
(*token) += e;
|
(*token) += e;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int parseInt(const char **token) {
|
static inline int parseInt(const char **token) {
|
||||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
skip_space(token);
|
||||||
int i = my_atoi((*token));
|
int i = my_atoi((*token));
|
||||||
(*token) += until_space((*token)); // strcspn((*token), " \t\r");
|
(*token) += until_space((*token));
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,9 +598,9 @@ assemble :
|
|||||||
|
|
||||||
{
|
{
|
||||||
// = pow(5.0, exponent);
|
// = pow(5.0, exponent);
|
||||||
double a = 5.0;
|
double a = 1.0;
|
||||||
for (int i = 0; i < exponent; i++) {
|
for (int i = 0; i < exponent; i++) {
|
||||||
a = a * a;
|
a = a * 5.0;
|
||||||
}
|
}
|
||||||
*result =
|
*result =
|
||||||
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
|
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
|
||||||
@@ -601,13 +614,12 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline float parseFloat(const char **token) {
|
static inline float parseFloat(const char **token) {
|
||||||
skip_space(token); //(*token) += strspn((*token), " \t");
|
skip_space(token);
|
||||||
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
|
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
|
||||||
float f = static_cast<float>(atof(*token));
|
float f = static_cast<float>(atof(*token));
|
||||||
(*token) += strcspn((*token), " \t\r");
|
(*token) += strcspn((*token), " \t\r");
|
||||||
#else
|
#else
|
||||||
const char *end =
|
const char *end = (*token) + until_space((*token));
|
||||||
(*token) + until_space((*token)); // strcspn((*token), " \t\r");
|
|
||||||
double val = 0.0;
|
double val = 0.0;
|
||||||
tryParseDouble((*token), end, &val);
|
tryParseDouble((*token), end, &val);
|
||||||
float f = static_cast<float>(val);
|
float f = static_cast<float>(val);
|
||||||
@@ -665,6 +677,11 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
|
|
||||||
std::string linebuf(&buf[0]);
|
std::string linebuf(&buf[0]);
|
||||||
|
|
||||||
|
// Trim trailing whitespace.
|
||||||
|
if (linebuf.size() > 0) {
|
||||||
|
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Trim newline '\r\n' or '\n'
|
// Trim newline '\r\n' or '\n'
|
||||||
if (linebuf.size() > 0) {
|
if (linebuf.size() > 0) {
|
||||||
if (linebuf[linebuf.size() - 1] == '\n')
|
if (linebuf[linebuf.size() - 1] == '\n')
|
||||||
@@ -747,7 +764,8 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transmittance
|
// transmittance
|
||||||
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
|
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
|
||||||
|
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
|
||||||
token += 2;
|
token += 2;
|
||||||
float r, g, b;
|
float r, g, b;
|
||||||
parseFloat3(&r, &g, &b, &token);
|
parseFloat3(&r, &g, &b, &token);
|
||||||
@@ -795,6 +813,7 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
material.dissolve = parseFloat(&token);
|
material.dissolve = parseFloat(&token);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
|
if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
|
||||||
token += 2;
|
token += 2;
|
||||||
// Invert value of Tr(assume Tr is in range [0, 1])
|
// Invert value of Tr(assume Tr is in range [0, 1])
|
||||||
@@ -802,6 +821,55 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PBR: roughness
|
||||||
|
if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.roughness = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: metallic
|
||||||
|
if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.metallic = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: sheen
|
||||||
|
if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.sheen = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: clearcoat thickness
|
||||||
|
if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.clearcoat_thickness = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: clearcoat roughness
|
||||||
|
if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) {
|
||||||
|
token += 4;
|
||||||
|
material.clearcoat_roughness = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: anisotropy
|
||||||
|
if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) {
|
||||||
|
token += 6;
|
||||||
|
material.anisotropy = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: anisotropy rotation
|
||||||
|
if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.anisotropy_rotation = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// ambient texture
|
// ambient texture
|
||||||
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
|
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
|
||||||
token += 7;
|
token += 7;
|
||||||
@@ -858,6 +926,41 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PBR: roughness texture
|
||||||
|
if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.roughness_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: metallic texture
|
||||||
|
if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.metallic_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: sheen texture
|
||||||
|
if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.sheen_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: emissive texture
|
||||||
|
if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.emissive_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: normal map texture
|
||||||
|
if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
|
||||||
|
token += 5;
|
||||||
|
material.normal_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// unknown parameter
|
// unknown parameter
|
||||||
const char *_space = strchr(token, ' ');
|
const char *_space = strchr(token, ' ');
|
||||||
if (!_space) {
|
if (!_space) {
|
||||||
@@ -896,8 +999,8 @@ typedef struct {
|
|||||||
float tx, ty;
|
float tx, ty;
|
||||||
|
|
||||||
// for f
|
// for f
|
||||||
std::vector<vertex_index, lt::allocator<vertex_index> > f;
|
std::vector<index_t, lt::allocator<index_t> > f;
|
||||||
// std::vector<vertex_index> f;
|
// std::vector<index_t> f;
|
||||||
std::vector<int, lt::allocator<int> > f_num_verts;
|
std::vector<int, lt::allocator<int> > f_num_verts;
|
||||||
|
|
||||||
const char *group_name;
|
const char *group_name;
|
||||||
@@ -918,44 +1021,42 @@ struct CommandCount {
|
|||||||
size_t num_vn;
|
size_t num_vn;
|
||||||
size_t num_vt;
|
size_t num_vt;
|
||||||
size_t num_f;
|
size_t num_f;
|
||||||
size_t num_faces;
|
size_t num_indices;
|
||||||
CommandCount() {
|
CommandCount() {
|
||||||
num_v = 0;
|
num_v = 0;
|
||||||
num_vn = 0;
|
num_vn = 0;
|
||||||
num_vt = 0;
|
num_vt = 0;
|
||||||
num_f = 0;
|
num_f = 0;
|
||||||
num_faces = 0;
|
num_indices = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class
|
class LoadOption {
|
||||||
LoadOption
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
|
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
|
||||||
|
|
||||||
int req_num_threads;
|
int req_num_threads;
|
||||||
bool triangulate;
|
bool triangulate;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Parse wavefront .obj(.obj string data is expanded to linear char array
|
/// Parse wavefront .obj(.obj string data is expanded to linear char array
|
||||||
/// `buf')
|
/// `buf')
|
||||||
/// -1 to req_num_threads use the number of HW threads in the running system.
|
/// -1 to req_num_threads use the number of HW threads in the running system.
|
||||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||||
size_t len, const LoadOption& option);
|
std::vector<material_t> *materials, const char *buf, size_t len,
|
||||||
|
const LoadOption &option);
|
||||||
|
|
||||||
#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
||||||
|
|
||||||
static bool parseLine(Command *command, const char *p, size_t p_len,
|
static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||||
bool triangulate = true) {
|
bool triangulate = true) {
|
||||||
|
// @todo { operate directly on pointer `p'. to do that, add range check for
|
||||||
|
// string operatoion against `p', since `p' is not null-terminated at p[p_len]
|
||||||
|
// }
|
||||||
char linebuf[4096];
|
char linebuf[4096];
|
||||||
assert(p_len < 4095);
|
assert(p_len < 4095);
|
||||||
// StackVector<char, 256> linebuf;
|
memcpy(linebuf, p, p_len);
|
||||||
// linebuf->resize(p_len + 1);
|
|
||||||
memcpy(&linebuf, p, p_len);
|
|
||||||
linebuf[p_len] = '\0';
|
linebuf[p_len] = '\0';
|
||||||
|
|
||||||
const char *token = linebuf;
|
const char *token = linebuf;
|
||||||
@@ -963,8 +1064,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
|||||||
command->type = COMMAND_EMPTY;
|
command->type = COMMAND_EMPTY;
|
||||||
|
|
||||||
// Skip leading space.
|
// Skip leading space.
|
||||||
// token += strspn(token, " \t");
|
skip_space(&token);
|
||||||
skip_space(&token); //(*token) += strspn((*token), " \t");
|
|
||||||
|
|
||||||
assert(token);
|
assert(token);
|
||||||
if (token[0] == '\0') { // empty line
|
if (token[0] == '\0') { // empty line
|
||||||
@@ -978,7 +1078,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
|||||||
// vertex
|
// vertex
|
||||||
if (token[0] == 'v' && IS_SPACE((token[1]))) {
|
if (token[0] == 'v' && IS_SPACE((token[1]))) {
|
||||||
token += 2;
|
token += 2;
|
||||||
float x, y, z;
|
float x = 0.0f, y = 0.0f, z = 0.0f;
|
||||||
parseFloat3(&x, &y, &z, &token);
|
parseFloat3(&x, &y, &z, &token);
|
||||||
command->vx = x;
|
command->vx = x;
|
||||||
command->vy = y;
|
command->vy = y;
|
||||||
@@ -990,7 +1090,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
|||||||
// normal
|
// normal
|
||||||
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
|
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
|
||||||
token += 3;
|
token += 3;
|
||||||
float x, y, z;
|
float x = 0.0f, y = 0.0f, z = 0.0f;
|
||||||
parseFloat3(&x, &y, &z, &token);
|
parseFloat3(&x, &y, &z, &token);
|
||||||
command->nx = x;
|
command->nx = x;
|
||||||
command->ny = y;
|
command->ny = y;
|
||||||
@@ -1002,7 +1102,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
|||||||
// texcoord
|
// texcoord
|
||||||
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
|
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||||
token += 3;
|
token += 3;
|
||||||
float x, y;
|
float x = 0.0f, y = 0.0f;
|
||||||
parseFloat2(&x, &y, &token);
|
parseFloat2(&x, &y, &token);
|
||||||
command->tx = x;
|
command->tx = x;
|
||||||
command->ty = y;
|
command->ty = y;
|
||||||
@@ -1013,19 +1113,12 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
|||||||
// face
|
// face
|
||||||
if (token[0] == 'f' && IS_SPACE((token[1]))) {
|
if (token[0] == 'f' && IS_SPACE((token[1]))) {
|
||||||
token += 2;
|
token += 2;
|
||||||
// token += strspn(token, " \t");
|
|
||||||
skip_space(&token);
|
skip_space(&token);
|
||||||
|
|
||||||
StackVector<vertex_index, 8> f;
|
StackVector<index_t, 8> f;
|
||||||
|
|
||||||
while (!IS_NEW_LINE(token[0])) {
|
while (!IS_NEW_LINE(token[0])) {
|
||||||
vertex_index vi = parseRawTriple(&token);
|
index_t vi = parseRawTriple(&token);
|
||||||
// printf("v = %d, %d, %d\n", vi.v_idx, vi.vn_idx, vi.vt_idx);
|
|
||||||
// if (callback.index_cb) {
|
|
||||||
// callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx);
|
|
||||||
//}
|
|
||||||
// size_t n = strspn(token, " \t\r");
|
|
||||||
// token += n;
|
|
||||||
skip_space_and_cr(&token);
|
skip_space_and_cr(&token);
|
||||||
|
|
||||||
f->push_back(vi);
|
f->push_back(vi);
|
||||||
@@ -1034,9 +1127,9 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
|
|||||||
command->type = COMMAND_F;
|
command->type = COMMAND_F;
|
||||||
|
|
||||||
if (triangulate) {
|
if (triangulate) {
|
||||||
vertex_index i0 = f[0];
|
index_t i0 = f[0];
|
||||||
vertex_index i1(-1);
|
index_t i1(-1);
|
||||||
vertex_index i2 = f[1];
|
index_t i2 = f[1];
|
||||||
|
|
||||||
for (size_t k = 2; k < f->size(); k++) {
|
for (size_t k = 2; k < f->size(); k++) {
|
||||||
i1 = i2;
|
i1 = i2;
|
||||||
@@ -1153,20 +1246,21 @@ static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||||
size_t len, const LoadOption& option)
|
std::vector<material_t> *materials, const char *buf, size_t len,
|
||||||
{
|
const LoadOption &option) {
|
||||||
attrib->vertices.clear();
|
attrib->vertices.clear();
|
||||||
attrib->normals.clear();
|
attrib->normals.clear();
|
||||||
attrib->texcoords.clear();
|
attrib->texcoords.clear();
|
||||||
attrib->faces.clear();
|
attrib->indices.clear();
|
||||||
attrib->face_num_verts.clear();
|
attrib->face_num_verts.clear();
|
||||||
attrib->material_ids.clear();
|
attrib->material_ids.clear();
|
||||||
shapes->clear();
|
shapes->clear();
|
||||||
|
|
||||||
if (len < 1) return false;
|
if (len < 1) return false;
|
||||||
|
|
||||||
auto num_threads = (option.req_num_threads < 0) ? std::thread::hardware_concurrency()
|
auto num_threads = (option.req_num_threads < 0)
|
||||||
|
? std::thread::hardware_concurrency()
|
||||||
: option.req_num_threads;
|
: option.req_num_threads;
|
||||||
num_threads =
|
num_threads =
|
||||||
std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
|
std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
|
||||||
@@ -1304,7 +1398,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
command_count[t].num_vt++;
|
command_count[t].num_vt++;
|
||||||
} else if (command.type == COMMAND_F) {
|
} else if (command.type == COMMAND_F) {
|
||||||
command_count[t].num_f += command.f.size();
|
command_count[t].num_f += command.f.size();
|
||||||
command_count[t].num_faces++;
|
command_count[t].num_indices += command.f_num_verts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.type == COMMAND_MTLLIB) {
|
if (command.type == COMMAND_MTLLIB) {
|
||||||
@@ -1329,7 +1423,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, int> material_map;
|
std::map<std::string, int> material_map;
|
||||||
std::vector<material_t> materials;
|
|
||||||
|
|
||||||
// Load material(if exits)
|
// Load material(if exits)
|
||||||
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
|
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
|
||||||
@@ -1344,7 +1437,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
|
|
||||||
std::ifstream ifs(material_filename);
|
std::ifstream ifs(material_filename);
|
||||||
if (ifs.good()) {
|
if (ifs.good()) {
|
||||||
LoadMtl(&material_map, &materials, &ifs);
|
LoadMtl(&material_map, materials, &ifs);
|
||||||
|
|
||||||
// std::cout << "maetrials = " << materials.size() << std::endl;
|
// std::cout << "maetrials = " << materials.size() << std::endl;
|
||||||
|
|
||||||
@@ -1368,14 +1461,15 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
size_t num_vn = 0;
|
size_t num_vn = 0;
|
||||||
size_t num_vt = 0;
|
size_t num_vt = 0;
|
||||||
size_t num_f = 0;
|
size_t num_f = 0;
|
||||||
size_t num_faces = 0;
|
size_t num_indices = 0;
|
||||||
for (size_t t = 0; t < num_threads; t++) {
|
for (size_t t = 0; t < num_threads; t++) {
|
||||||
num_v += command_count[t].num_v;
|
num_v += command_count[t].num_v;
|
||||||
num_vn += command_count[t].num_vn;
|
num_vn += command_count[t].num_vn;
|
||||||
num_vt += command_count[t].num_vt;
|
num_vt += command_count[t].num_vt;
|
||||||
num_f += command_count[t].num_f;
|
num_f += command_count[t].num_f;
|
||||||
num_faces += command_count[t].num_faces;
|
num_indices += command_count[t].num_indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "# v " << num_v << std::endl;
|
// std::cout << "# v " << num_v << std::endl;
|
||||||
// std::cout << "# vn " << num_vn << std::endl;
|
// std::cout << "# vn " << num_vn << std::endl;
|
||||||
// std::cout << "# vt " << num_vt << std::endl;
|
// std::cout << "# vt " << num_vt << std::endl;
|
||||||
@@ -1389,9 +1483,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
attrib->vertices.resize(num_v * 3);
|
attrib->vertices.resize(num_v * 3);
|
||||||
attrib->normals.resize(num_vn * 3);
|
attrib->normals.resize(num_vn * 3);
|
||||||
attrib->texcoords.resize(num_vt * 2);
|
attrib->texcoords.resize(num_vt * 2);
|
||||||
attrib->faces.resize(num_f);
|
attrib->indices.resize(num_f);
|
||||||
attrib->face_num_verts.resize(num_faces);
|
attrib->face_num_verts.resize(num_indices);
|
||||||
attrib->material_ids.resize(num_faces);
|
attrib->material_ids.resize(num_indices);
|
||||||
|
|
||||||
size_t v_offsets[kMaxThreads];
|
size_t v_offsets[kMaxThreads];
|
||||||
size_t n_offsets[kMaxThreads];
|
size_t n_offsets[kMaxThreads];
|
||||||
@@ -1410,7 +1504,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn;
|
n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn;
|
||||||
t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt;
|
t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt;
|
||||||
f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f;
|
f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f;
|
||||||
face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_faces;
|
face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
StackVector<std::thread, 16> workers;
|
StackVector<std::thread, 16> workers;
|
||||||
@@ -1456,17 +1550,20 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
t_count++;
|
t_count++;
|
||||||
} else if (commands[t][i].type == COMMAND_F) {
|
} else if (commands[t][i].type == COMMAND_F) {
|
||||||
for (size_t k = 0; k < commands[t][i].f.size(); k++) {
|
for (size_t k = 0; k < commands[t][i].f.size(); k++) {
|
||||||
vertex_index &vi = commands[t][i].f[k];
|
index_t &vi = commands[t][i].f[k];
|
||||||
int v_idx = fixIndex(vi.v_idx, v_count);
|
int vertex_index = fixIndex(vi.vertex_index, v_count);
|
||||||
int vn_idx = fixIndex(vi.vn_idx, n_count);
|
int texcoord_index = fixIndex(vi.texcoord_index, t_count);
|
||||||
int vt_idx = fixIndex(vi.vt_idx, t_count);
|
int normal_index = fixIndex(vi.normal_index, n_count);
|
||||||
attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
|
attrib->indices[f_count + k] =
|
||||||
|
index_t(vertex_index, texcoord_index, normal_index);
|
||||||
|
}
|
||||||
|
for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) {
|
||||||
|
attrib->material_ids[face_count + k] = material_id;
|
||||||
|
attrib->face_num_verts[face_count + k] = commands[t][i].f_num_verts[k];
|
||||||
}
|
}
|
||||||
attrib->material_ids[face_count] = material_id;
|
|
||||||
attrib->face_num_verts[face_count] = commands[t][i].f.size();
|
|
||||||
|
|
||||||
f_count += commands[t][i].f.size();
|
f_count += commands[t][i].f.size();
|
||||||
face_count++;
|
face_count += commands[t][i].f_num_verts.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -1567,8 +1664,8 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
|||||||
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
|
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
|
||||||
std::cout << "# of normals = " << attrib->normals.size() << std::endl;
|
std::cout << "# of normals = " << attrib->normals.size() << std::endl;
|
||||||
std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl;
|
std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl;
|
||||||
std::cout << "# of face indices = " << attrib->faces.size() << std::endl;
|
std::cout << "# of face indices = " << attrib->indices.size() << std::endl;
|
||||||
std::cout << "# of faces = " << attrib->material_ids.size() << std::endl;
|
std::cout << "# of indices = " << attrib->material_ids.size() << std::endl;
|
||||||
std::cout << "# of shapes = " << shapes->size() << std::endl;
|
std::cout << "# of shapes = " << shapes->size() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void vcopy(const float *v1, float *v2) {
|
static void vcopy(const float *v1, float *v2) {
|
||||||
register int i;
|
int i;
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
v2[i] = v1[i];
|
v2[i] = v1[i];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,11 +214,11 @@ const char* get_file_data(size_t *len, const char* filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads)
|
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose)
|
||||||
{
|
{
|
||||||
#if 1
|
|
||||||
tinyobj_opt::attrib_t attrib;
|
tinyobj_opt::attrib_t attrib;
|
||||||
std::vector<tinyobj_opt::shape_t> shapes;
|
std::vector<tinyobj_opt::shape_t> shapes;
|
||||||
|
std::vector<tinyobj_opt::material_t> materials;
|
||||||
|
|
||||||
size_t data_len = 0;
|
size_t data_len = 0;
|
||||||
const char* data = get_file_data(&data_len, filename);
|
const char* data = get_file_data(&data_len, filename);
|
||||||
@@ -229,11 +229,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
|||||||
printf("filesize: %d\n", (int)data_len);
|
printf("filesize: %d\n", (int)data_len);
|
||||||
tinyobj_opt::LoadOption option;
|
tinyobj_opt::LoadOption option;
|
||||||
option.req_num_threads = num_threads;
|
option.req_num_threads = num_threads;
|
||||||
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
|
option.verbose = verbose;
|
||||||
|
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
|
||||||
|
|
||||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
//std::cout << "vertices.size() = " << attrib.vertices.size() << std::endl;
|
||||||
|
//std::cout << "normals.size() = " << attrib.normals.size() << std::endl;
|
||||||
|
|
||||||
{
|
{
|
||||||
DrawObject o;
|
DrawObject o;
|
||||||
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
||||||
@@ -241,15 +245,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
|||||||
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
|
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
|
||||||
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face.
|
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face.
|
||||||
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
|
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
|
||||||
tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0];
|
tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0];
|
||||||
tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1];
|
tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1];
|
||||||
tinyobj_opt::vertex_index idx2 = attrib.faces[face_offset+3*f+2];
|
tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2];
|
||||||
|
|
||||||
float v[3][3];
|
float v[3][3];
|
||||||
for (int k = 0; k < 3; k++) {
|
for (int k = 0; k < 3; k++) {
|
||||||
int f0 = idx0.v_idx;
|
int f0 = idx0.vertex_index;
|
||||||
int f1 = idx1.v_idx;
|
int f1 = idx1.vertex_index;
|
||||||
int f2 = idx2.v_idx;
|
int f2 = idx2.vertex_index;
|
||||||
assert(f0 >= 0);
|
assert(f0 >= 0);
|
||||||
assert(f1 >= 0);
|
assert(f1 >= 0);
|
||||||
assert(f2 >= 0);
|
assert(f2 >= 0);
|
||||||
@@ -268,19 +272,24 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
|||||||
float n[3][3];
|
float n[3][3];
|
||||||
|
|
||||||
if (attrib.normals.size() > 0) {
|
if (attrib.normals.size() > 0) {
|
||||||
int f0 = idx0.vn_idx;
|
int nf0 = idx0.normal_index;
|
||||||
int f1 = idx1.vn_idx;
|
int nf1 = idx1.normal_index;
|
||||||
int f2 = idx2.vn_idx;
|
int nf2 = idx2.normal_index;
|
||||||
assert(f0 >= 0);
|
|
||||||
assert(f1 >= 0);
|
if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) {
|
||||||
assert(f2 >= 0);
|
assert(3*nf0+2 < attrib.normals.size());
|
||||||
assert(3*f0+2 < attrib.normals.size());
|
assert(3*nf1+2 < attrib.normals.size());
|
||||||
assert(3*f1+2 < attrib.normals.size());
|
assert(3*nf2+2 < attrib.normals.size());
|
||||||
assert(3*f2+2 < attrib.normals.size());
|
|
||||||
for (int k = 0; k < 3; k++) {
|
for (int k = 0; k < 3; k++) {
|
||||||
n[0][k] = attrib.normals[3*f0+k];
|
n[0][k] = attrib.normals[3*nf0+k];
|
||||||
n[1][k] = attrib.normals[3*f1+k];
|
n[1][k] = attrib.normals[3*nf1+k];
|
||||||
n[2][k] = attrib.normals[3*f2+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 {
|
} else {
|
||||||
// compute geometric normal
|
// compute geometric normal
|
||||||
@@ -299,7 +308,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
|||||||
// Use normal as color.
|
// Use normal as color.
|
||||||
float c[3] = {n[k][0], n[k][1], n[k][2]};
|
float c[3] = {n[k][0], n[k][1], n[k][2]};
|
||||||
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
|
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
|
||||||
if (len2 > 0.0f) {
|
if (len2 > 1.0e-6f) {
|
||||||
float len = sqrtf(len2);
|
float len = sqrtf(len2);
|
||||||
|
|
||||||
c[0] /= len;
|
c[0] /= len;
|
||||||
@@ -330,9 +339,6 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
|||||||
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
|
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reshapeFunc(GLFWwindow* window, int w, int h)
|
void reshapeFunc(GLFWwindow* window, int w, int h)
|
||||||
@@ -499,24 +505,31 @@ static void Init() {
|
|||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
std::cout << "Needs input.obj\n" << std::endl;
|
std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool benchmark_only = false;
|
bool benchmark_only = false;
|
||||||
int num_threads = -1;
|
int num_threads = -1;
|
||||||
|
bool verbose = false;
|
||||||
|
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
num_threads = atoi(argv[2]);
|
num_threads = atoi(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 3) {
|
if (argc > 3) {
|
||||||
benchmark_only = true;
|
benchmark_only = (atoi(argv[3]) > 0) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 4) {
|
||||||
|
verbose = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (benchmark_only) {
|
if (benchmark_only) {
|
||||||
|
|
||||||
tinyobj_opt::attrib_t attrib;
|
tinyobj_opt::attrib_t attrib;
|
||||||
std::vector<tinyobj_opt::shape_t> shapes;
|
std::vector<tinyobj_opt::shape_t> shapes;
|
||||||
|
std::vector<tinyobj_opt::material_t> materials;
|
||||||
|
|
||||||
size_t data_len = 0;
|
size_t data_len = 0;
|
||||||
const char* data = get_file_data(&data_len, argv[1]);
|
const char* data = get_file_data(&data_len, argv[1]);
|
||||||
@@ -527,7 +540,9 @@ int main(int argc, char **argv)
|
|||||||
printf("filesize: %d\n", (int)data_len);
|
printf("filesize: %d\n", (int)data_len);
|
||||||
tinyobj_opt::LoadOption option;
|
tinyobj_opt::LoadOption option;
|
||||||
option.req_num_threads = num_threads;
|
option.req_num_threads = num_threads;
|
||||||
bool ret = parseObj(&attrib, &shapes, data, data_len, option);
|
option.verbose = true;
|
||||||
|
|
||||||
|
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -569,7 +584,7 @@ int main(int argc, char **argv)
|
|||||||
reshapeFunc(window, width, height);
|
reshapeFunc(window, width, height);
|
||||||
|
|
||||||
float bmin[3], bmax[3];
|
float bmin[3], bmax[3];
|
||||||
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads)) {
|
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) {
|
||||||
printf("failed to load & conv\n");
|
printf("failed to load & conv\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,19 @@
|
|||||||
#define TINYOBJLOADER_IMPLEMENTATION
|
#define TINYOBJLOADER_IMPLEMENTATION
|
||||||
#include "tiny_obj_loader.h"
|
#include "tiny_obj_loader.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cassert>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
#include <windows.h>
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -58,7 +58,8 @@ public:
|
|||||||
static_cast<time_t>((tv[1].tv_usec - tv[0].tv_usec) / 1000);
|
static_cast<time_t>((tv[1].tv_usec - tv[0].tv_usec) / 1000);
|
||||||
}
|
}
|
||||||
time_t usec() {
|
time_t usec() {
|
||||||
return this->sec() * 1000000 + static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
|
return this->sec() * 1000000 +
|
||||||
|
static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec);
|
||||||
}
|
}
|
||||||
time_t current() {
|
time_t current() {
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
@@ -94,11 +95,13 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials)
|
static void PrintInfo(const tinyobj::attrib_t& attrib,
|
||||||
{
|
const std::vector<tinyobj::shape_t>& shapes,
|
||||||
|
const std::vector<tinyobj::material_t>& materials) {
|
||||||
std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl;
|
std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl;
|
||||||
std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl;
|
std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl;
|
||||||
std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl;
|
std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2)
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
std::cout << "# of shapes : " << shapes.size() << std::endl;
|
std::cout << "# of shapes : " << shapes.size() << std::endl;
|
||||||
std::cout << "# of materials : " << materials.size() << std::endl;
|
std::cout << "# of materials : " << materials.size() << std::endl;
|
||||||
@@ -125,63 +128,68 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
|||||||
|
|
||||||
// For each shape
|
// For each shape
|
||||||
for (size_t i = 0; i < shapes.size(); i++) {
|
for (size_t i = 0; i < shapes.size(); i++) {
|
||||||
printf("shape[%ld].name = %s\n", static_cast<long>(i), shapes[i].name.c_str());
|
printf("shape[%ld].name = %s\n", static_cast<long>(i),
|
||||||
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.indices.size()));
|
shapes[i].name.c_str());
|
||||||
|
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i),
|
||||||
|
static_cast<unsigned long>(shapes[i].mesh.indices.size()));
|
||||||
|
|
||||||
size_t index_offset = 0;
|
size_t index_offset = 0;
|
||||||
|
|
||||||
assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.material_ids.size());
|
assert(shapes[i].mesh.num_face_vertices.size() ==
|
||||||
|
shapes[i].mesh.material_ids.size());
|
||||||
|
|
||||||
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
|
printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i),
|
||||||
|
static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size()));
|
||||||
|
|
||||||
// For each face
|
// For each face
|
||||||
for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) {
|
for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) {
|
||||||
size_t fnum = shapes[i].mesh.num_face_vertices[f];
|
size_t fnum = shapes[i].mesh.num_face_vertices[f];
|
||||||
|
|
||||||
printf(" face[%ld].fnum = %ld\n", static_cast<long>(f), static_cast<unsigned long>(fnum));
|
printf(" face[%ld].fnum = %ld\n", static_cast<long>(f),
|
||||||
|
static_cast<unsigned long>(fnum));
|
||||||
|
|
||||||
// For each vertex in the face
|
// For each vertex in the face
|
||||||
for (size_t v = 0; v < fnum; v++) {
|
for (size_t v = 0; v < fnum; v++) {
|
||||||
tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v];
|
tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v];
|
||||||
printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast<long>(f), static_cast<long>(v), idx.vertex_index, idx.normal_index, idx.texcoord_index);
|
printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast<long>(f),
|
||||||
|
static_cast<long>(v), idx.vertex_index, idx.normal_index,
|
||||||
|
idx.texcoord_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" face[%ld].material_id = %d\n", static_cast<long>(f), shapes[i].mesh.material_ids[f]);
|
printf(" face[%ld].material_id = %d\n", static_cast<long>(f),
|
||||||
|
shapes[i].mesh.material_ids[f]);
|
||||||
|
|
||||||
index_offset += fnum;
|
index_offset += fnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i), static_cast<unsigned long>(shapes[i].mesh.tags.size()));
|
printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i),
|
||||||
|
static_cast<unsigned long>(shapes[i].mesh.tags.size()));
|
||||||
for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) {
|
for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) {
|
||||||
printf(" tag[%ld] = %s ", static_cast<long>(t), shapes[i].mesh.tags[t].name.c_str());
|
printf(" tag[%ld] = %s ", static_cast<long>(t),
|
||||||
|
shapes[i].mesh.tags[t].name.c_str());
|
||||||
printf(" ints: [");
|
printf(" ints: [");
|
||||||
for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j)
|
for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) {
|
||||||
{
|
|
||||||
printf("%ld", static_cast<long>(shapes[i].mesh.tags[t].intValues[j]));
|
printf("%ld", static_cast<long>(shapes[i].mesh.tags[t].intValues[j]));
|
||||||
if (j < (shapes[i].mesh.tags[t].intValues.size()-1))
|
if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) {
|
||||||
{
|
|
||||||
printf(", ");
|
printf(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("]");
|
printf("]");
|
||||||
|
|
||||||
printf(" floats: [");
|
printf(" floats: [");
|
||||||
for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j)
|
for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) {
|
||||||
{
|
printf("%f", static_cast<const double>(
|
||||||
printf("%f", static_cast<const double>(shapes[i].mesh.tags[t].floatValues[j]));
|
shapes[i].mesh.tags[t].floatValues[j]));
|
||||||
if (j < (shapes[i].mesh.tags[t].floatValues.size()-1))
|
if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) {
|
||||||
{
|
|
||||||
printf(", ");
|
printf(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("]");
|
printf("]");
|
||||||
|
|
||||||
printf(" strings: [");
|
printf(" strings: [");
|
||||||
for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j)
|
for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) {
|
||||||
{
|
|
||||||
printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str());
|
printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str());
|
||||||
if (j < (shapes[i].mesh.tags[t].stringValues.size()-1))
|
if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) {
|
||||||
{
|
|
||||||
printf(", ");
|
printf(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,25 +199,59 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < materials.size(); i++) {
|
for (size_t i = 0; i < materials.size(); i++) {
|
||||||
printf("material[%ld].name = %s\n", static_cast<long>(i), materials[i].name.c_str());
|
printf("material[%ld].name = %s\n", static_cast<long>(i),
|
||||||
printf(" material.Ka = (%f, %f ,%f)\n", static_cast<const double>(materials[i].ambient[0]), static_cast<const double>(materials[i].ambient[1]), static_cast<const double>(materials[i].ambient[2]));
|
materials[i].name.c_str());
|
||||||
printf(" material.Kd = (%f, %f ,%f)\n", static_cast<const double>(materials[i].diffuse[0]), static_cast<const double>(materials[i].diffuse[1]), static_cast<const double>(materials[i].diffuse[2]));
|
printf(" material.Ka = (%f, %f ,%f)\n",
|
||||||
printf(" material.Ks = (%f, %f ,%f)\n", static_cast<const double>(materials[i].specular[0]), static_cast<const double>(materials[i].specular[1]), static_cast<const double>(materials[i].specular[2]));
|
static_cast<const double>(materials[i].ambient[0]),
|
||||||
printf(" material.Tr = (%f, %f ,%f)\n", static_cast<const double>(materials[i].transmittance[0]), static_cast<const double>(materials[i].transmittance[1]), static_cast<const double>(materials[i].transmittance[2]));
|
static_cast<const double>(materials[i].ambient[1]),
|
||||||
printf(" material.Ke = (%f, %f ,%f)\n", static_cast<const double>(materials[i].emission[0]), static_cast<const double>(materials[i].emission[1]), static_cast<const double>(materials[i].emission[2]));
|
static_cast<const double>(materials[i].ambient[2]));
|
||||||
printf(" material.Ns = %f\n", static_cast<const double>(materials[i].shininess));
|
printf(" material.Kd = (%f, %f ,%f)\n",
|
||||||
|
static_cast<const double>(materials[i].diffuse[0]),
|
||||||
|
static_cast<const double>(materials[i].diffuse[1]),
|
||||||
|
static_cast<const double>(materials[i].diffuse[2]));
|
||||||
|
printf(" material.Ks = (%f, %f ,%f)\n",
|
||||||
|
static_cast<const double>(materials[i].specular[0]),
|
||||||
|
static_cast<const double>(materials[i].specular[1]),
|
||||||
|
static_cast<const double>(materials[i].specular[2]));
|
||||||
|
printf(" material.Tr = (%f, %f ,%f)\n",
|
||||||
|
static_cast<const double>(materials[i].transmittance[0]),
|
||||||
|
static_cast<const double>(materials[i].transmittance[1]),
|
||||||
|
static_cast<const double>(materials[i].transmittance[2]));
|
||||||
|
printf(" material.Ke = (%f, %f ,%f)\n",
|
||||||
|
static_cast<const double>(materials[i].emission[0]),
|
||||||
|
static_cast<const double>(materials[i].emission[1]),
|
||||||
|
static_cast<const double>(materials[i].emission[2]));
|
||||||
|
printf(" material.Ns = %f\n",
|
||||||
|
static_cast<const double>(materials[i].shininess));
|
||||||
printf(" material.Ni = %f\n", static_cast<const double>(materials[i].ior));
|
printf(" material.Ni = %f\n", static_cast<const double>(materials[i].ior));
|
||||||
printf(" material.dissolve = %f\n", static_cast<const double>(materials[i].dissolve));
|
printf(" material.dissolve = %f\n",
|
||||||
|
static_cast<const double>(materials[i].dissolve));
|
||||||
printf(" material.illum = %d\n", materials[i].illum);
|
printf(" material.illum = %d\n", materials[i].illum);
|
||||||
printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
|
printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
|
||||||
printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
|
printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
|
||||||
printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str());
|
printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str());
|
||||||
printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str());
|
printf(" material.map_Ns = %s\n",
|
||||||
|
materials[i].specular_highlight_texname.c_str());
|
||||||
printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str());
|
printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str());
|
||||||
printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str());
|
printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str());
|
||||||
printf(" material.disp = %s\n", materials[i].displacement_texname.c_str());
|
printf(" material.disp = %s\n", materials[i].displacement_texname.c_str());
|
||||||
std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
|
printf(" <<PBR>>\n");
|
||||||
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
|
printf(" material.Pr = %f\n", materials[i].roughness);
|
||||||
|
printf(" material.Pm = %f\n", materials[i].metallic);
|
||||||
|
printf(" material.Ps = %f\n", materials[i].sheen);
|
||||||
|
printf(" material.Pc = %f\n", materials[i].clearcoat_thickness);
|
||||||
|
printf(" material.Pcr = %f\n", materials[i].clearcoat_thickness);
|
||||||
|
printf(" material.aniso = %f\n", materials[i].anisotropy);
|
||||||
|
printf(" material.anisor = %f\n", materials[i].anisotropy_rotation);
|
||||||
|
printf(" material.map_Ke = %s\n", materials[i].emissive_texname.c_str());
|
||||||
|
printf(" material.map_Pr = %s\n", materials[i].roughness_texname.c_str());
|
||||||
|
printf(" material.map_Pm = %s\n", materials[i].metallic_texname.c_str());
|
||||||
|
printf(" material.map_Ps = %s\n", materials[i].sheen_texname.c_str());
|
||||||
|
printf(" material.norm = %s\n", materials[i].normal_texname.c_str());
|
||||||
|
std::map<std::string, std::string>::const_iterator it(
|
||||||
|
materials[i].unknown_parameter.begin());
|
||||||
|
std::map<std::string, std::string>::const_iterator itEnd(
|
||||||
|
materials[i].unknown_parameter.end());
|
||||||
|
|
||||||
for (; it != itEnd; it++) {
|
for (; it != itEnd; it++) {
|
||||||
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
|
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
|
||||||
@@ -218,12 +260,8 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector<tinyobj
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool TestLoadObj(const char* filename, const char* basepath = NULL,
|
||||||
TestLoadObj(
|
bool triangulate = true) {
|
||||||
const char* filename,
|
|
||||||
const char* basepath = NULL,
|
|
||||||
bool triangulate = true)
|
|
||||||
{
|
|
||||||
std::cout << "Loading " << filename << std::endl;
|
std::cout << "Loading " << filename << std::endl;
|
||||||
|
|
||||||
tinyobj::attrib_t attrib;
|
tinyobj::attrib_t attrib;
|
||||||
@@ -233,7 +271,8 @@ TestLoadObj(
|
|||||||
timerutil t;
|
timerutil t;
|
||||||
t.start();
|
t.start();
|
||||||
std::string err;
|
std::string err;
|
||||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate);
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename,
|
||||||
|
basepath, triangulate);
|
||||||
t.end();
|
t.end();
|
||||||
printf("Parsing time: %lu [msecs]\n", t.msec());
|
printf("Parsing time: %lu [msecs]\n", t.msec());
|
||||||
|
|
||||||
@@ -251,15 +290,11 @@ TestLoadObj(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool TestStreamLoadObj() {
|
||||||
static bool
|
|
||||||
TestStreamLoadObj()
|
|
||||||
{
|
|
||||||
std::cout << "Stream Loading " << std::endl;
|
std::cout << "Stream Loading " << std::endl;
|
||||||
|
|
||||||
std::stringstream objStream;
|
std::stringstream objStream;
|
||||||
objStream
|
objStream << "mtllib cube.mtl\n"
|
||||||
<< "mtllib cube.mtl\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"v 0.000000 2.000000 2.000000\n"
|
"v 0.000000 2.000000 2.000000\n"
|
||||||
"v 0.000000 0.000000 2.000000\n"
|
"v 0.000000 0.000000 2.000000\n"
|
||||||
@@ -318,18 +353,15 @@ std::string matStream(
|
|||||||
"Ks 0 0 0");
|
"Ks 0 0 0");
|
||||||
|
|
||||||
using namespace tinyobj;
|
using namespace tinyobj;
|
||||||
class MaterialStringStreamReader:
|
class MaterialStringStreamReader : public MaterialReader {
|
||||||
public MaterialReader
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
MaterialStringStreamReader(const std::string& matSStream): m_matSStream(matSStream) {}
|
MaterialStringStreamReader(const std::string& matSStream)
|
||||||
|
: m_matSStream(matSStream) {}
|
||||||
virtual ~MaterialStringStreamReader() {}
|
virtual ~MaterialStringStreamReader() {}
|
||||||
virtual bool operator() (
|
virtual bool operator()(const std::string& matId,
|
||||||
const std::string& matId,
|
|
||||||
std::vector<material_t>* materials,
|
std::vector<material_t>* materials,
|
||||||
std::map<std::string, int>* matMap,
|
std::map<std::string, int>* matMap,
|
||||||
std::string* err)
|
std::string* err) {
|
||||||
{
|
|
||||||
(void)matId;
|
(void)matId;
|
||||||
(void)err;
|
(void)err;
|
||||||
LoadMtl(matMap, materials, &m_matSStream);
|
LoadMtl(matMap, materials, &m_matSStream);
|
||||||
@@ -345,7 +377,8 @@ std::string matStream(
|
|||||||
std::vector<tinyobj::shape_t> shapes;
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
std::string err;
|
std::string err;
|
||||||
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader);
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream,
|
||||||
|
&matSSReader);
|
||||||
|
|
||||||
if (!err.empty()) {
|
if (!err.empty()) {
|
||||||
std::cerr << err << std::endl;
|
std::cerr << err << std::endl;
|
||||||
@@ -360,11 +393,7 @@ std::string matStream(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int main(int argc, char** argv) {
|
||||||
main(
|
|
||||||
int argc,
|
|
||||||
char **argv)
|
|
||||||
{
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
const char* basepath = "models/";
|
const char* basepath = "models/";
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
@@ -375,7 +404,8 @@ main(
|
|||||||
// assert(true == TestLoadObj("cornell_box.obj"));
|
// assert(true == TestLoadObj("cornell_box.obj"));
|
||||||
// assert(true == TestLoadObj("cube.obj"));
|
// assert(true == TestLoadObj("cube.obj"));
|
||||||
assert(true == TestStreamLoadObj());
|
assert(true == TestStreamLoadObj());
|
||||||
assert(true == TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
|
assert(true ==
|
||||||
|
TestLoadObj("models/catmark_torus_creases0.obj", "models/", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
6
models/issue-92.mtl
Normal file
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
|
||||||
173
python/main.cpp
173
python/main.cpp
@@ -5,16 +5,17 @@
|
|||||||
// model = tol.LoadObj(name)
|
// model = tol.LoadObj(name)
|
||||||
// print(model["shapes"])
|
// print(model["shapes"])
|
||||||
// print(model["materials"]
|
// print(model["materials"]
|
||||||
|
// note:
|
||||||
|
// `shape.mesh.index_t` is represented as flattened array: (vertex_index, normal_index, texcoord_index) * num_faces
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "../tiny_obj_loader.h"
|
#include "../tiny_obj_loader.h"
|
||||||
|
|
||||||
typedef std::vector<double> vectd;
|
typedef std::vector<double> vectd;
|
||||||
|
typedef std::vector<int> vecti;
|
||||||
|
|
||||||
PyObject*
|
PyObject* pyTupleFromfloat3(float array[3]) {
|
||||||
pyTupleFromfloat3 (float array[3])
|
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
PyObject* tuple = PyTuple_New(3);
|
PyObject* tuple = PyTuple_New(3);
|
||||||
|
|
||||||
@@ -25,103 +26,145 @@ pyTupleFromfloat3 (float array[3])
|
|||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
|
||||||
pyLoadObj(PyObject* self, PyObject* args)
|
PyObject *rtndict, *pyshapes, *pymaterials, *attribobj, *current, *meshobj;
|
||||||
{
|
|
||||||
PyObject *rtndict, *pyshapes, *pymaterials,
|
|
||||||
*current, *meshobj;
|
|
||||||
|
|
||||||
|
char const* current_name;
|
||||||
char const* filename;
|
char const* filename;
|
||||||
char *current_name;
|
|
||||||
vectd vect;
|
vectd vect;
|
||||||
|
std::vector<tinyobj::index_t> indices;
|
||||||
|
std::vector<unsigned char> face_verts;
|
||||||
|
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
std::vector<tinyobj::shape_t> shapes;
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
if(!PyArg_ParseTuple(args, "s", &filename))
|
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
std::string err;
|
std::string err;
|
||||||
tinyobj::LoadObj(shapes, materials, err, filename);
|
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
|
||||||
|
|
||||||
pyshapes = PyDict_New();
|
pyshapes = PyDict_New();
|
||||||
pymaterials = PyDict_New();
|
pymaterials = PyDict_New();
|
||||||
rtndict = PyDict_New();
|
rtndict = PyDict_New();
|
||||||
|
|
||||||
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin() ;
|
attribobj = PyDict_New();
|
||||||
shape != shapes.end(); shape++)
|
|
||||||
{
|
|
||||||
meshobj = PyDict_New();
|
|
||||||
tinyobj::mesh_t cm = (*shape).mesh;
|
|
||||||
|
|
||||||
for (int i = 0; i <= 4; i++ )
|
for (int i = 0; i <= 2; i++) {
|
||||||
{
|
|
||||||
current = PyList_New(0);
|
current = PyList_New(0);
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
current_name = "positions";
|
current_name = "vertices";
|
||||||
vect = vectd(cm.positions.begin(), cm.positions.end()); break;
|
vect = vectd(attrib.vertices.begin(), attrib.vertices.end());
|
||||||
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
current_name = "normals";
|
current_name = "normals";
|
||||||
vect = vectd(cm.normals.begin(), cm.normals.end()); break;
|
vect = vectd(attrib.normals.begin(), attrib.normals.end());
|
||||||
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
current_name = "texcoords";
|
current_name = "texcoords";
|
||||||
vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); break;
|
vect = vectd(attrib.texcoords.begin(), attrib.texcoords.end());
|
||||||
case 3:
|
break;
|
||||||
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() ;
|
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
|
||||||
it != vect.end(); it++)
|
|
||||||
{
|
|
||||||
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
|
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(meshobj, current_name, current);
|
PyDict_SetItemString(attribobj, current_name, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
|
||||||
|
shape != shapes.end(); shape++) {
|
||||||
|
meshobj = PyDict_New();
|
||||||
|
tinyobj::mesh_t cm = (*shape).mesh;
|
||||||
|
|
||||||
|
{
|
||||||
|
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);
|
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
|
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
|
||||||
mat != materials.end(); mat++)
|
mat != materials.end(); mat++) {
|
||||||
{
|
|
||||||
PyObject* matobj = PyDict_New();
|
PyObject* matobj = PyDict_New();
|
||||||
PyObject* unknown_parameter = PyDict_New();
|
PyObject* unknown_parameter = PyDict_New();
|
||||||
|
|
||||||
for (std::map<std::string, std::string>::iterator p = (*mat).unknown_parameter.begin() ;
|
for (std::map<std::string, std::string>::iterator p =
|
||||||
p != (*mat).unknown_parameter.end(); ++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(unknown_parameter, p->first.c_str(),
|
||||||
|
PyUnicode_FromString(p->second.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(matobj, "shininess", PyFloat_FromDouble((*mat).shininess));
|
PyDict_SetItemString(matobj, "shininess",
|
||||||
|
PyFloat_FromDouble((*mat).shininess));
|
||||||
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
|
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
|
||||||
PyDict_SetItemString(matobj, "dissolve", PyFloat_FromDouble((*mat).dissolve));
|
PyDict_SetItemString(matobj, "dissolve",
|
||||||
|
PyFloat_FromDouble((*mat).dissolve));
|
||||||
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
|
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
|
||||||
PyDict_SetItemString(matobj, "ambient_texname", PyUnicode_FromString((*mat).ambient_texname.c_str()));
|
PyDict_SetItemString(matobj, "ambient_texname",
|
||||||
PyDict_SetItemString(matobj, "diffuse_texname", PyUnicode_FromString((*mat).diffuse_texname.c_str()));
|
PyUnicode_FromString((*mat).ambient_texname.c_str()));
|
||||||
PyDict_SetItemString(matobj, "specular_texname", PyUnicode_FromString((*mat).specular_texname.c_str()));
|
PyDict_SetItemString(matobj, "diffuse_texname",
|
||||||
PyDict_SetItemString(matobj, "specular_highlight_texname", PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
|
PyUnicode_FromString((*mat).diffuse_texname.c_str()));
|
||||||
PyDict_SetItemString(matobj, "bump_texname", PyUnicode_FromString((*mat).bump_texname.c_str()));
|
PyDict_SetItemString(matobj, "specular_texname",
|
||||||
PyDict_SetItemString(matobj, "displacement_texname", PyUnicode_FromString((*mat).displacement_texname.c_str()));
|
PyUnicode_FromString((*mat).specular_texname.c_str()));
|
||||||
PyDict_SetItemString(matobj, "alpha_texname", PyUnicode_FromString((*mat).alpha_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, "ambient", pyTupleFromfloat3((*mat).ambient));
|
||||||
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
|
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
|
||||||
PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3((*mat).specular));
|
PyDict_SetItemString(matobj, "specular",
|
||||||
PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3((*mat).transmittance));
|
pyTupleFromfloat3((*mat).specular));
|
||||||
PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3((*mat).emission));
|
PyDict_SetItemString(matobj, "transmittance",
|
||||||
|
pyTupleFromfloat3((*mat).transmittance));
|
||||||
|
PyDict_SetItemString(matobj, "emission",
|
||||||
|
pyTupleFromfloat3((*mat).emission));
|
||||||
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
||||||
|
|
||||||
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
|
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
|
||||||
@@ -133,28 +176,16 @@ pyLoadObj(PyObject* self, PyObject* args)
|
|||||||
return rtndict;
|
return rtndict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef mMethods[] = {
|
static PyMethodDef mMethods[] = {
|
||||||
|
|
||||||
{"LoadObj", pyLoadObj, METH_VARARGS},
|
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
|
||||||
{NULL, NULL, 0, NULL}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
|
||||||
|
NULL, -1, mMethods};
|
||||||
|
|
||||||
static struct PyModuleDef moduledef = {
|
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
|
||||||
PyModuleDef_HEAD_INIT,
|
|
||||||
"tinyobjloader",
|
|
||||||
NULL,
|
|
||||||
-1,
|
|
||||||
mMethods
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
|
||||||
PyInit_tinyobjloader(void)
|
|
||||||
{
|
|
||||||
return PyModule_Create(&moduledef);
|
return PyModule_Create(&moduledef);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ std::string matStream(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* gMtlBasePath = "../models";
|
const char* gMtlBasePath = "../models/";
|
||||||
|
|
||||||
TEST_CASE("cornell_box", "[Loader]") {
|
TEST_CASE("cornell_box", "[Loader]") {
|
||||||
|
|
||||||
@@ -319,10 +319,107 @@ TEST_CASE("catmark_torus_creases0", "[Loader]") {
|
|||||||
REQUIRE(8 == shapes[0].mesh.tags.size());
|
REQUIRE(8 == shapes[0].mesh.tags.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pbr", "[Loader]") {
|
||||||
|
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/pbr-mat-ext.obj", gMtlBasePath, /*triangulate*/false);
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
REQUIRE(1 == materials.size());
|
||||||
|
REQUIRE(0.2 == Approx(materials[0].roughness));
|
||||||
|
REQUIRE(0.3 == Approx(materials[0].metallic));
|
||||||
|
REQUIRE(0.4 == Approx(materials[0].sheen));
|
||||||
|
REQUIRE(0.5 == Approx(materials[0].clearcoat_thickness));
|
||||||
|
REQUIRE(0.6 == Approx(materials[0].clearcoat_roughness));
|
||||||
|
REQUIRE(0.7 == Approx(materials[0].anisotropy));
|
||||||
|
REQUIRE(0.8 == Approx(materials[0].anisotropy_rotation));
|
||||||
|
REQUIRE(0 == materials[0].roughness_texname.compare("roughness.tex"));
|
||||||
|
REQUIRE(0 == materials[0].metallic_texname.compare("metallic.tex"));
|
||||||
|
REQUIRE(0 == materials[0].sheen_texname.compare("sheen.tex"));
|
||||||
|
REQUIRE(0 == materials[0].emissive_texname.compare("emissive.tex"));
|
||||||
|
REQUIRE(0 == materials[0].normal_texname.compare("normalmap.tex"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("stream_load", "[Stream]") {
|
TEST_CASE("stream_load", "[Stream]") {
|
||||||
REQUIRE(true == TestStreamLoadObj());
|
REQUIRE(true == TestStreamLoadObj());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("trailing_whitespace_in_mtl", "[Issue92]") {
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-92.obj", gMtlBasePath);
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
REQUIRE(1 == materials.size());
|
||||||
|
REQUIRE(0 == materials[0].diffuse_texname.compare("tmp.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transmittance_filter", "[Issue95]") {
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath);
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
REQUIRE(1 == materials.size());
|
||||||
|
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
|
||||||
|
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
|
||||||
|
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transmittance_filter_Tf", "[Issue95-Tf]") {
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95-2.obj", gMtlBasePath);
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
REQUIRE(1 == materials.size());
|
||||||
|
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
|
||||||
|
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
|
||||||
|
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
|
||||||
|
}
|
||||||
|
TEST_CASE("transmittance_filter_Kt", "[Issue95-Kt]") {
|
||||||
|
tinyobj::attrib_t attrib;
|
||||||
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath);
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
REQUIRE(1 == materials.size());
|
||||||
|
REQUIRE(0.1 == Approx(materials[0].transmittance[0]));
|
||||||
|
REQUIRE(0.2 == Approx(materials[0].transmittance[1]));
|
||||||
|
REQUIRE(0.3 == Approx(materials[0].transmittance[2]));
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
int
|
int
|
||||||
main(
|
main(
|
||||||
|
|||||||
@@ -24,33 +24,6 @@ THE SOFTWARE.
|
|||||||
|
|
||||||
//
|
//
|
||||||
// version 1.0.0 : Change data structure. Change license from BSD to MIT.
|
// version 1.0.0 : Change data structure. Change license from BSD to MIT.
|
||||||
// Support different index for
|
|
||||||
// vertex/normal/texcoord(#73, #39)
|
|
||||||
// version 0.9.20: Fixes creating per-face material using `usemtl`(#68)
|
|
||||||
// version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension)
|
|
||||||
// version 0.9.16: Make tinyobjloader header-only
|
|
||||||
// version 0.9.15: Change API to handle no mtl file case correctly(#58)
|
|
||||||
// version 0.9.14: Support specular highlight, bump, displacement and alpha
|
|
||||||
// map(#53)
|
|
||||||
// version 0.9.13: Report "Material file not found message" in `err`(#46)
|
|
||||||
// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before
|
|
||||||
// 'g' (#44)
|
|
||||||
// version 0.9.11: Invert `Tr` parameter(#43)
|
|
||||||
// version 0.9.10: Fix seg fault on windows.
|
|
||||||
// version 0.9.9 : Replace atof() with custom parser.
|
|
||||||
// version 0.9.8 : Fix multi-materials(per-face material ID).
|
|
||||||
// version 0.9.7 : Support multi-materials(per-face material ID) per
|
|
||||||
// object/group.
|
|
||||||
// version 0.9.6 : Support Ni(index of refraction) mtl parameter.
|
|
||||||
// Parse transmittance material parameter correctly.
|
|
||||||
// version 0.9.5 : Parse multiple group name.
|
|
||||||
// Add support of specifying the base path to load material
|
|
||||||
// file.
|
|
||||||
// version 0.9.4 : Initial support of group tag(g)
|
|
||||||
// version 0.9.3 : Fix parsing triple 'x/y/z'
|
|
||||||
// version 0.9.2 : Add more .mtl load support
|
|
||||||
// version 0.9.1 : Add initial .mtl load support
|
|
||||||
// version 0.9.0 : Initial
|
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -62,9 +35,9 @@ THE SOFTWARE.
|
|||||||
#ifndef TINY_OBJ_LOADER_H_
|
#ifndef TINY_OBJ_LOADER_H_
|
||||||
#define TINY_OBJ_LOADER_H_
|
#define TINY_OBJ_LOADER_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace tinyobj {
|
namespace tinyobj {
|
||||||
|
|
||||||
@@ -91,6 +64,22 @@ typedef struct {
|
|||||||
std::string bump_texname; // map_bump, bump
|
std::string bump_texname; // map_bump, bump
|
||||||
std::string displacement_texname; // disp
|
std::string displacement_texname; // disp
|
||||||
std::string alpha_texname; // map_d
|
std::string alpha_texname; // map_d
|
||||||
|
|
||||||
|
// PBR extension
|
||||||
|
// http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
|
||||||
|
float roughness; // [0, 1] default 0
|
||||||
|
float metallic; // [0, 1] default 0
|
||||||
|
float sheen; // [0, 1] default 0
|
||||||
|
float clearcoat_thickness; // [0, 1] default 0
|
||||||
|
float clearcoat_roughness; // [0, 1] default 0
|
||||||
|
float anisotropy; // aniso. [0, 1] default 0
|
||||||
|
float anisotropy_rotation; // anisor. [0, 1] default 0
|
||||||
|
std::string roughness_texname; // map_Pr
|
||||||
|
std::string metallic_texname; // map_Pm
|
||||||
|
std::string sheen_texname; // map_Ps
|
||||||
|
std::string emissive_texname; // map_Ke
|
||||||
|
std::string normal_texname; // norm. For normal mapping.
|
||||||
|
|
||||||
std::map<std::string, std::string> unknown_parameter;
|
std::map<std::string, std::string> unknown_parameter;
|
||||||
} material_t;
|
} material_t;
|
||||||
|
|
||||||
@@ -112,8 +101,9 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
std::vector<index_t> indices;
|
std::vector<index_t> indices;
|
||||||
std::vector<unsigned char>
|
std::vector<unsigned char> num_face_vertices; // The number of vertices per
|
||||||
num_face_vertices; // The number of vertices per face. 3 = polygon, 4 = quad, ... Up to 255.
|
// face. 3 = polygon, 4 = quad,
|
||||||
|
// ... Up to 255.
|
||||||
std::vector<int> material_ids; // per-face material ID
|
std::vector<int> material_ids; // per-face material ID
|
||||||
std::vector<tag_t> tags; // SubD tag
|
std::vector<tag_t> tags; // SubD tag
|
||||||
} mesh_t;
|
} mesh_t;
|
||||||
@@ -131,14 +121,22 @@ typedef struct {
|
|||||||
} attrib_t;
|
} attrib_t;
|
||||||
|
|
||||||
typedef struct callback_t_ {
|
typedef struct callback_t_ {
|
||||||
void (*vertex_cb)(void *user_data, float x, float y, float z);
|
// W is optional and set to 1 if there is no `w` item in `v` line
|
||||||
|
void (*vertex_cb)(void *user_data, float x, float y, float z, float w);
|
||||||
void (*normal_cb)(void *user_data, float x, float y, float z);
|
void (*normal_cb)(void *user_data, float x, float y, float z);
|
||||||
void (*texcoord_cb)(void *user_data, float x, float y);
|
|
||||||
// -2147483648 will be passed for undefined index
|
// y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in
|
||||||
void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx);
|
// `vt` line.
|
||||||
// `name` material name, `materialId` = the array index of material_t[]. -1 if
|
void (*texcoord_cb)(void *user_data, float x, float y, float z);
|
||||||
|
|
||||||
|
// called per 'f' line. num_indices is the number of face indices(e.g. 3 for
|
||||||
|
// triangle, 4 for quad)
|
||||||
|
// 0 will be passed for undefined index in index_t members.
|
||||||
|
void (*index_cb)(void *user_data, index_t *indices, int num_indices);
|
||||||
|
// `name` material name, `material_id` = the array index of material_t[]. -1
|
||||||
|
// if
|
||||||
// a material not found in .mtl
|
// a material not found in .mtl
|
||||||
void (*usemtl_cb)(void *user_data, const char *name, int materialId);
|
void (*usemtl_cb)(void *user_data, const char *name, int material_id);
|
||||||
// `materials` = parsed material data.
|
// `materials` = parsed material data.
|
||||||
void (*mtllib_cb)(void *user_data, const material_t *materials,
|
void (*mtllib_cb)(void *user_data, const material_t *materials,
|
||||||
int num_materials);
|
int num_materials);
|
||||||
@@ -155,7 +153,6 @@ typedef struct callback_t_ {
|
|||||||
mtllib_cb(NULL),
|
mtllib_cb(NULL),
|
||||||
group_cb(NULL),
|
group_cb(NULL),
|
||||||
object_cb(NULL) {}
|
object_cb(NULL) {}
|
||||||
|
|
||||||
} callback_t;
|
} callback_t;
|
||||||
|
|
||||||
class MaterialReader {
|
class MaterialReader {
|
||||||
@@ -200,12 +197,11 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
/// `callback.mtllib_cb`.
|
/// `callback.mtllib_cb`.
|
||||||
/// Returns true when loading .obj/.mtl become success.
|
/// Returns true when loading .obj/.mtl become success.
|
||||||
/// Returns warning and error message into `err`
|
/// Returns warning and error message into `err`
|
||||||
/// 'mtl_basepath' is optional, and used for base path for .mtl file.
|
/// See `examples/callback_api/` for how to use this function.
|
||||||
/// 'triangulate' is optional, and used whether triangulate polygon face in .obj
|
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
|
||||||
/// or not.
|
void *user_data = NULL,
|
||||||
bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
MaterialReader *readMatFn = NULL,
|
||||||
std::string *err, std::istream *inStream,
|
std::string *err = NULL);
|
||||||
MaterialReader *readMatFn);
|
|
||||||
|
|
||||||
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
|
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
|
||||||
/// std::istream for materials.
|
/// std::istream for materials.
|
||||||
@@ -223,12 +219,12 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
} // namespace tinyobj
|
} // namespace tinyobj
|
||||||
|
|
||||||
#ifdef TINYOBJLOADER_IMPLEMENTATION
|
#ifdef TINYOBJLOADER_IMPLEMENTATION
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cctype>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -261,6 +257,38 @@ struct obj_shape {
|
|||||||
std::vector<float> vt;
|
std::vector<float> vt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// See
|
||||||
|
// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
|
||||||
|
static std::istream &safeGetline(std::istream &is, std::string &t) {
|
||||||
|
t.clear();
|
||||||
|
|
||||||
|
// The characters in the stream are read one-by-one using a std::streambuf.
|
||||||
|
// That is faster than reading them one-by-one using the std::istream.
|
||||||
|
// Code that uses streambuf this way must be guarded by a sentry object.
|
||||||
|
// The sentry object performs various tasks,
|
||||||
|
// such as thread synchronization and updating the stream state.
|
||||||
|
|
||||||
|
std::istream::sentry se(is, true);
|
||||||
|
std::streambuf *sb = is.rdbuf();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int c = sb->sbumpc();
|
||||||
|
switch (c) {
|
||||||
|
case '\n':
|
||||||
|
return is;
|
||||||
|
case '\r':
|
||||||
|
if (sb->sgetc() == '\n') sb->sbumpc();
|
||||||
|
return is;
|
||||||
|
case EOF:
|
||||||
|
// Also handle the case when the last line has no line ending
|
||||||
|
if (t.empty()) is.setstate(std::ios::eofbit);
|
||||||
|
return is;
|
||||||
|
default:
|
||||||
|
t += static_cast<char>(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
|
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
|
||||||
#define IS_DIGIT(x) \
|
#define IS_DIGIT(x) \
|
||||||
(static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
|
(static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
|
||||||
@@ -424,18 +452,13 @@ fail:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float parseFloat(const char **token) {
|
static inline float parseFloat(const char **token, double default_value = 0.0) {
|
||||||
(*token) += strspn((*token), " \t");
|
(*token) += strspn((*token), " \t");
|
||||||
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
|
|
||||||
float f = static_cast<float>(atof(*token));
|
|
||||||
(*token) += strcspn((*token), " \t\r");
|
|
||||||
#else
|
|
||||||
const char *end = (*token) + strcspn((*token), " \t\r");
|
const char *end = (*token) + strcspn((*token), " \t\r");
|
||||||
double val = 0.0;
|
double val = default_value;
|
||||||
tryParseDouble((*token), end, &val);
|
tryParseDouble((*token), end, &val);
|
||||||
float f = static_cast<float>(val);
|
float f = static_cast<float>(val);
|
||||||
(*token) = end;
|
(*token) = end;
|
||||||
#endif
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,6 +474,14 @@ static inline void parseFloat3(float *x, float *y, float *z,
|
|||||||
(*z) = parseFloat(token);
|
(*z) = parseFloat(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void parseV(float *x, float *y, float *z, float *w,
|
||||||
|
const char **token) {
|
||||||
|
(*x) = parseFloat(token);
|
||||||
|
(*y) = parseFloat(token);
|
||||||
|
(*z) = parseFloat(token);
|
||||||
|
(*w) = parseFloat(token, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
static tag_sizes parseTagTriple(const char **token) {
|
static tag_sizes parseTagTriple(const char **token) {
|
||||||
tag_sizes ts;
|
tag_sizes ts;
|
||||||
|
|
||||||
@@ -510,8 +541,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize,
|
|||||||
|
|
||||||
// Parse raw triples: i, i/j/k, i//k, i/j
|
// Parse raw triples: i, i/j/k, i//k, i/j
|
||||||
static vertex_index parseRawTriple(const char **token) {
|
static vertex_index parseRawTriple(const char **token) {
|
||||||
vertex_index vi(
|
vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
|
||||||
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
|
|
||||||
|
|
||||||
vi.v_idx = atoi((*token));
|
vi.v_idx = atoi((*token));
|
||||||
(*token) += strcspn((*token), "/ \t\r");
|
(*token) += strcspn((*token), "/ \t\r");
|
||||||
@@ -562,6 +592,20 @@ static void InitMaterial(material_t *material) {
|
|||||||
material->dissolve = 1.f;
|
material->dissolve = 1.f;
|
||||||
material->shininess = 1.f;
|
material->shininess = 1.f;
|
||||||
material->ior = 1.f;
|
material->ior = 1.f;
|
||||||
|
|
||||||
|
material->roughness = 0.f;
|
||||||
|
material->metallic = 0.f;
|
||||||
|
material->sheen = 0.f;
|
||||||
|
material->clearcoat_thickness = 0.f;
|
||||||
|
material->clearcoat_roughness = 0.f;
|
||||||
|
material->anisotropy_rotation = 0.f;
|
||||||
|
material->anisotropy = 0.f;
|
||||||
|
material->roughness_texname = "";
|
||||||
|
material->metallic_texname = "";
|
||||||
|
material->sheen_texname = "";
|
||||||
|
material->emissive_texname = "";
|
||||||
|
material->normal_texname = "";
|
||||||
|
|
||||||
material->unknown_parameter.clear();
|
material->unknown_parameter.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,7 +660,8 @@ static bool exportFaceGroupToShape(
|
|||||||
shape->mesh.indices.push_back(idx);
|
shape->mesh.indices.push_back(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
shape->mesh.num_face_vertices.push_back(static_cast<unsigned char>(npolys));
|
shape->mesh.num_face_vertices.push_back(
|
||||||
|
static_cast<unsigned char>(npolys));
|
||||||
shape->mesh.material_ids.push_back(material_id); // per face
|
shape->mesh.material_ids.push_back(material_id); // per face
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -633,12 +678,15 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
material_t material;
|
material_t material;
|
||||||
InitMaterial(&material);
|
InitMaterial(&material);
|
||||||
|
|
||||||
size_t maxchars = 8192; // Alloc enough size.
|
|
||||||
std::vector<char> buf(maxchars); // Alloc enough size.
|
|
||||||
while (inStream->peek() != -1) {
|
while (inStream->peek() != -1) {
|
||||||
inStream->getline(&buf[0], static_cast<std::streamsize>(maxchars));
|
std::string linebuf;
|
||||||
|
|
||||||
std::string linebuf(&buf[0]);
|
safeGetline(*inStream, linebuf);
|
||||||
|
|
||||||
|
// Trim trailing whitespace.
|
||||||
|
if (linebuf.size() > 0) {
|
||||||
|
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Trim newline '\r\n' or '\n'
|
// Trim newline '\r\n' or '\n'
|
||||||
if (linebuf.size() > 0) {
|
if (linebuf.size() > 0) {
|
||||||
@@ -722,7 +770,8 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transmittance
|
// transmittance
|
||||||
if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
|
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
|
||||||
|
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
|
||||||
token += 2;
|
token += 2;
|
||||||
float r, g, b;
|
float r, g, b;
|
||||||
parseFloat3(&r, &g, &b, &token);
|
parseFloat3(&r, &g, &b, &token);
|
||||||
@@ -777,6 +826,55 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PBR: roughness
|
||||||
|
if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.roughness = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: metallic
|
||||||
|
if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.metallic = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: sheen
|
||||||
|
if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.sheen = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: clearcoat thickness
|
||||||
|
if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) {
|
||||||
|
token += 2;
|
||||||
|
material.clearcoat_thickness = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: clearcoat roughness
|
||||||
|
if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) {
|
||||||
|
token += 4;
|
||||||
|
material.clearcoat_roughness = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: anisotropy
|
||||||
|
if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) {
|
||||||
|
token += 6;
|
||||||
|
material.anisotropy = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: anisotropy rotation
|
||||||
|
if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.anisotropy_rotation = parseFloat(&token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// ambient texture
|
// ambient texture
|
||||||
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
|
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
|
||||||
token += 7;
|
token += 7;
|
||||||
@@ -833,6 +931,41 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PBR: roughness texture
|
||||||
|
if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.roughness_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: metallic texture
|
||||||
|
if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.metallic_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: sheen texture
|
||||||
|
if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.sheen_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: emissive texture
|
||||||
|
if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
|
||||||
|
token += 7;
|
||||||
|
material.emissive_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PBR: normal map texture
|
||||||
|
if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
|
||||||
|
token += 5;
|
||||||
|
material.normal_texname = token;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// unknown parameter
|
// unknown parameter
|
||||||
const char *_space = strchr(token, ' ');
|
const char *_space = strchr(token, ' ');
|
||||||
if (!_space) {
|
if (!_space) {
|
||||||
@@ -926,12 +1059,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
|
|
||||||
shape_t shape;
|
shape_t shape;
|
||||||
|
|
||||||
int maxchars = 8192; // Alloc enough size.
|
|
||||||
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
|
|
||||||
while (inStream->peek() != -1) {
|
while (inStream->peek() != -1) {
|
||||||
inStream->getline(&buf[0], maxchars);
|
std::string linebuf;
|
||||||
|
safeGetline(*inStream, linebuf);
|
||||||
std::string linebuf(&buf[0]);
|
|
||||||
|
|
||||||
// Trim newline '\r\n' or '\n'
|
// Trim newline '\r\n' or '\n'
|
||||||
if (linebuf.size() > 0) {
|
if (linebuf.size() > 0) {
|
||||||
@@ -1193,21 +1323,26 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
|
||||||
std::string *err, std::istream *inStream,
|
void *user_data /*= NULL*/,
|
||||||
MaterialReader *readMatFn) {
|
MaterialReader *readMatFn /*= NULL*/,
|
||||||
|
std::string *err /*= NULL*/) {
|
||||||
std::stringstream errss;
|
std::stringstream errss;
|
||||||
|
|
||||||
// material
|
// material
|
||||||
std::map<std::string, int> material_map;
|
std::map<std::string, int> material_map;
|
||||||
int materialId = -1; // -1 = invalid
|
int material_id = -1; // -1 = invalid
|
||||||
|
|
||||||
int maxchars = 8192; // Alloc enough size.
|
std::vector<index_t> indices;
|
||||||
std::vector<char> buf(static_cast<size_t>(maxchars)); // Alloc enough size.
|
std::vector<material_t> materials;
|
||||||
while (inStream->peek() != -1) {
|
std::vector<std::string> names;
|
||||||
inStream->getline(&buf[0], maxchars);
|
names.reserve(2);
|
||||||
|
std::string name;
|
||||||
|
std::vector<const char *> names_out;
|
||||||
|
|
||||||
std::string linebuf(&buf[0]);
|
std::string linebuf;
|
||||||
|
while (inStream.peek() != -1) {
|
||||||
|
safeGetline(inStream, linebuf);
|
||||||
|
|
||||||
// Trim newline '\r\n' or '\n'
|
// Trim newline '\r\n' or '\n'
|
||||||
if (linebuf.size() > 0) {
|
if (linebuf.size() > 0) {
|
||||||
@@ -1236,10 +1371,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
// vertex
|
// vertex
|
||||||
if (token[0] == 'v' && IS_SPACE((token[1]))) {
|
if (token[0] == 'v' && IS_SPACE((token[1]))) {
|
||||||
token += 2;
|
token += 2;
|
||||||
float x, y, z;
|
float x, y, z, w; // w is optional. default = 1.0
|
||||||
parseFloat3(&x, &y, &z, &token);
|
parseV(&x, &y, &z, &w, &token);
|
||||||
if (callback.vertex_cb) {
|
if (callback.vertex_cb) {
|
||||||
callback.vertex_cb(user_data, x, y, z);
|
callback.vertex_cb(user_data, x, y, z, w);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1258,10 +1393,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
// texcoord
|
// texcoord
|
||||||
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
|
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
|
||||||
token += 3;
|
token += 3;
|
||||||
float x, y;
|
float x, y, z; // y and z are optional. default = 0.0
|
||||||
parseFloat2(&x, &y, &token);
|
parseFloat3(&x, &y, &z, &token);
|
||||||
if (callback.texcoord_cb) {
|
if (callback.texcoord_cb) {
|
||||||
callback.texcoord_cb(user_data, x, y);
|
callback.texcoord_cb(user_data, x, y, z);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1271,15 +1406,25 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
token += 2;
|
token += 2;
|
||||||
token += strspn(token, " \t");
|
token += strspn(token, " \t");
|
||||||
|
|
||||||
|
indices.clear();
|
||||||
while (!IS_NEW_LINE(token[0])) {
|
while (!IS_NEW_LINE(token[0])) {
|
||||||
vertex_index vi = parseRawTriple(&token);
|
vertex_index vi = parseRawTriple(&token);
|
||||||
if (callback.index_cb) {
|
|
||||||
callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx);
|
index_t idx;
|
||||||
}
|
idx.vertex_index = vi.v_idx;
|
||||||
|
idx.normal_index = vi.vn_idx;
|
||||||
|
idx.texcoord_index = vi.vt_idx;
|
||||||
|
|
||||||
|
indices.push_back(idx);
|
||||||
size_t n = strspn(token, " \t\r");
|
size_t n = strspn(token, " \t\r");
|
||||||
token += n;
|
token += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (callback.index_cb && indices.size() > 0) {
|
||||||
|
callback.index_cb(user_data, &indices.at(0),
|
||||||
|
static_cast<int>(indices.size()));
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1288,7 +1433,8 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
|
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
|
||||||
token += 7;
|
token += 7;
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
|
sscanf_s(token, "%s", namebuf,
|
||||||
|
static_cast<unsigned int>(_countof(namebuf)));
|
||||||
#else
|
#else
|
||||||
sscanf(token, "%s", namebuf);
|
sscanf(token, "%s", namebuf);
|
||||||
#endif
|
#endif
|
||||||
@@ -1300,12 +1446,12 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
// { error!! material not found }
|
// { error!! material not found }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newMaterialId != materialId) {
|
if (newMaterialId != material_id) {
|
||||||
materialId = newMaterialId;
|
material_id = newMaterialId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback.usemtl_cb) {
|
if (callback.usemtl_cb) {
|
||||||
callback.usemtl_cb(user_data, namebuf, materialId);
|
callback.usemtl_cb(user_data, namebuf, material_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -1313,6 +1459,7 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
|
|
||||||
// load mtl
|
// load mtl
|
||||||
if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
|
if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
|
||||||
|
if (readMatFn) {
|
||||||
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
|
char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
|
||||||
token += 7;
|
token += 7;
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@@ -1322,7 +1469,7 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string err_mtl;
|
std::string err_mtl;
|
||||||
std::vector<material_t> materials;
|
materials.clear();
|
||||||
bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
|
bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) += err_mtl;
|
(*err) += err_mtl;
|
||||||
@@ -1336,14 +1483,14 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
callback.mtllib_cb(user_data, &materials.at(0),
|
callback.mtllib_cb(user_data, &materials.at(0),
|
||||||
static_cast<int>(materials.size()));
|
static_cast<int>(materials.size()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// group name
|
// group name
|
||||||
if (token[0] == 'g' && IS_SPACE((token[1]))) {
|
if (token[0] == 'g' && IS_SPACE((token[1]))) {
|
||||||
std::vector<std::string> names;
|
names.clear();
|
||||||
names.reserve(2);
|
|
||||||
|
|
||||||
while (!IS_NEW_LINE(token[0])) {
|
while (!IS_NEW_LINE(token[0])) {
|
||||||
std::string str = parseString(&token);
|
std::string str = parseString(&token);
|
||||||
@@ -1353,28 +1500,26 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
|
|
||||||
assert(names.size() > 0);
|
assert(names.size() > 0);
|
||||||
|
|
||||||
std::string name;
|
|
||||||
// names[0] must be 'g', so skip the 0th element.
|
// names[0] must be 'g', so skip the 0th element.
|
||||||
if (names.size() > 1) {
|
if (names.size() > 1) {
|
||||||
name = names[1];
|
name = names[1];
|
||||||
} else {
|
} else {
|
||||||
name = "";
|
name.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback.group_cb) {
|
if (callback.group_cb) {
|
||||||
if (names.size() > 1) {
|
if (names.size() > 1) {
|
||||||
// create const char* array.
|
// create const char* array.
|
||||||
std::vector<const char *> tmp(names.size() - 1);
|
names_out.resize(names.size() - 1);
|
||||||
for (size_t j = 0; j < tmp.size(); j++) {
|
for (size_t j = 0; j < names_out.size(); j++) {
|
||||||
tmp[j] = names[j + 1].c_str();
|
names_out[j] = names[j + 1].c_str();
|
||||||
}
|
}
|
||||||
callback.group_cb(user_data, &tmp.at(0),
|
callback.group_cb(user_data, &names_out.at(0),
|
||||||
static_cast<int>(tmp.size()));
|
static_cast<int>(names_out.size()));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
callback.group_cb(user_data, NULL, 0);
|
callback.group_cb(user_data, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -1390,10 +1535,10 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback,
|
|||||||
#else
|
#else
|
||||||
sscanf(token, "%s", namebuf);
|
sscanf(token, "%s", namebuf);
|
||||||
#endif
|
#endif
|
||||||
std::string name = std::string(namebuf);
|
std::string object_name = std::string(namebuf);
|
||||||
|
|
||||||
if (callback.object_cb) {
|
if (callback.object_cb) {
|
||||||
callback.object_cb(user_data, name.c_str());
|
callback.object_cb(user_data, object_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user