6 Commits
v1.0.4 ... c89

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

View File

@@ -1,4 +1,5 @@
# tinyobjloader tinyobjloader
=============
[![Join the chat at https://gitter.im/syoyo/tinyobjloader](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/syoyo/tinyobjloader](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -16,24 +17,29 @@ Tiny but powerful single file wavefront obj loader written in C++. No dependency
`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) `tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-)
If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c .
Notice! What's new
----------
* XX YY, ZZZZ : New data strcutre and API!
* Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert!
* Nov 26, 2015 : Now single-header only!.
* Nov 08, 2015 : Improved API.
* Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors!
* Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel!
* Feb 06, 2015 : Fix parsing multi-material object
* Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo!
* Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran!
* Apr 29, 2014 : Add API to read .obj from std::istream. Good for reading compressed .obj or connecting to procedural primitive generator. Thanks burnse!
* Apr 21, 2014 : Define default material if no material definition exists in .obj. Thanks YarmUI!
* Apr 10, 2014 : Add support for parsing 'illum' and 'd'/'Tr' statements. Thanks mmp!
* Jan 27, 2014 : Added CMake project. Thanks bradc6!
* Nov 26, 2013 : Performance optimization by NeuralSandwich. 9% improvement in his project, thanks!
* Sep 12, 2013 : Added multiple .obj sticher example.
Example
------- -------
We have released new version v1.0.0 on 20 Aug, 2016.
Old version is available `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x
## What's new
* 20 Aug, 2016 : Bump version v1.0.0. New data strcutre and API!
### Old version
Previous old version is avaiable in `v0.9.x` branch.
## Example
![Rungholt](images/rungholt.jpg) ![Rungholt](images/rungholt.jpg)
tinyobjloader can successfully load 6M triangles Rungholt scene. tinyobjloader can successfully load 6M triangles Rungholt scene.
@@ -42,21 +48,12 @@ http://graphics.cs.williams.edu/data/meshes.xml
![](images/sanmugel.png) ![](images/sanmugel.png)
* [examples/viewer/](examples/viewer) OpenGL .obj viewer * [examples/viewer/](examples/viewer) OpenGL .obj viewer
* [examples/callback_api/](examples/callback_api/) Callback API example
* [examples/voxelize/](examples/voxelize/) Voxelizer example
## Use case Use case
--------
TinyObjLoader is successfully used in ... TinyObjLoader is successfully used in ...
### New version(v1.0.x)
* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models
* .obj viewer with Metal https://github.com/middlefeng/NuoModelViewer/tree/master
* 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
@@ -68,19 +65,10 @@ TinyObjLoader is successfully used in ...
* pbrt-v3 https://github.com/mmp/pbrt-v3 * pbrt-v3 https://github.com/mmp/pbrt-v3
* cocos2d-x https://github.com/cocos2d/cocos2d-x/ * cocos2d-x https://github.com/cocos2d/cocos2d-x/
* Android Vulkan demo https://github.com/SaschaWillems/Vulkan * Android Vulkan demo https://github.com/SaschaWillems/Vulkan
* voxelizer https://github.com/karimnaaji/voxelizer * Your project here!
* Probulator https://github.com/kayru/Probulator
* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking
* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK
* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg
* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl
* Point Based Global Illumination on modern GPU https://pbgi.wordpress.com/code-source/
* Fast OBJ file importing and parsing in CUDA http://researchonline.jcu.edu.au/42515/1/2015.CVM.OBJCUDA.pdf
* Sorted Shading for Uni-Directional Pathtracing by Joshua Bainbridge https://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc15/02Josh/joshua_bainbridge_thesis.pdf
* GeeXLab http://www.geeks3d.com/hacklab/20160531/geexlab-0-12-0-0-released-for-windows/
Features
## Features --------
* Group(parse multiple group name) * Group(parse multiple group name)
* Vertex * Vertex
@@ -89,23 +77,23 @@ TinyObjLoader is successfully used in ...
* Material * Material
* Unknown material attributes are returned as key-value(value is string) map. * Unknown material attributes are returned as key-value(value is string) map.
* Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) * Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification)
* PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
* 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.
* [ ] Texture options
* [ ] Normal vector generation
* [ ] Support smoothing groups
## 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`.
@@ -160,7 +148,8 @@ for (size_t s = 0; s < shapes.size(); s++) {
``` ```
## Optimized loader Optimized loader
----------------
Optimized multi-threaded .obj loader is available at `experimental/` directory. Optimized multi-threaded .obj loader is available at `experimental/` directory.
If you want absolute performance to load .obj data, this optimized loader will fit your purpose. If you want absolute performance to load .obj data, this optimized loader will fit your purpose.
@@ -174,6 +163,7 @@ Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core
* optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline) * optimised: 1500 msecs(10x faster than old version, 4.5x faster than basedline)
## Tests Tests
-----
Unit tests are provided in `tests` directory. See `tests/README.md` for details. Unit tests are provided in `tests` directory. See `tests/README.md` for details.

View File

@@ -1,4 +1,4 @@
version: 1.0.{build} version: 0.9.{build}
platform: x64 platform: x64
@@ -7,6 +7,7 @@ install:
# All external dependencies are installed in C:\projects\deps # All external dependencies are installed in C:\projects\deps
####################################################################################### #######################################################################################
- mkdir C:\projects\deps - mkdir C:\projects\deps
- cd C:\projects\deps
####################################################################################### #######################################################################################
# Install Ninja # Install Ninja

View File

@@ -1,44 +1,39 @@
//
// An example of how to use callback API.
// This example is minimum and incomplete. Just showing the usage of callback
// API.
// You need to implement your own Mesh data struct constrution based on this
// example in practical.
//
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h" #include "tiny_obj_loader.h"
#include <cassert>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <fstream>
typedef struct { typedef struct
{
std::vector<float> vertices; std::vector<float> vertices;
std::vector<float> normals; std::vector<float> normals;
std::vector<float> texcoords; std::vector<float> texcoords;
std::vector<int> v_indices; std::vector<int> v_indices;
std::vector<int> vn_indices; std::vector<int> vn_indices;
std::vector<int> vt_indices; std::vector<int> vt_indices;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
} MyMesh; } MyMesh;
void vertex_cb(void *user_data, float x, float y, float z, float w) { void vertex_cb(void *user_data, float x, float y, float z)
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); {
printf("v[%ld] = %f, %f, %f (w %f)\n", mesh->vertices.size() / 3, x, y, z, w); MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("v[%ld] = %f, %f, %f\n", mesh->vertices.size() / 3, x, y, z);
mesh->vertices.push_back(x); mesh->vertices.push_back(x);
mesh->vertices.push_back(y); mesh->vertices.push_back(y);
mesh->vertices.push_back(z); mesh->vertices.push_back(z);
// Discard w
} }
void normal_cb(void *user_data, float x, float y, float z) { void normal_cb(void *user_data, float x, float y, float z)
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); {
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z); printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z);
mesh->normals.push_back(x); mesh->normals.push_back(x);
@@ -46,56 +41,49 @@ void normal_cb(void *user_data, float x, float y, float z) {
mesh->normals.push_back(z); mesh->normals.push_back(z);
} }
void texcoord_cb(void *user_data, float x, float y, float z) { void texcoord_cb(void *user_data, float x, float y)
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); {
printf("vt[%ld] = %f, %f, %f\n", mesh->texcoords.size() / 3, x, y, z); MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("vt[%ld] = %f, %f\n", mesh->texcoords.size() / 2, x, y);
mesh->texcoords.push_back(x); mesh->texcoords.push_back(x);
mesh->texcoords.push_back(y); mesh->texcoords.push_back(y);
mesh->texcoords.push_back(z);
} }
void index_cb(void *user_data, tinyobj::index_t *indices, int num_indices) { void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx)
// NOTE: the value of each index is raw value. {
// NOTE: the value of each index is raw value.
// For example, the application must manually adjust the index with offset // For example, the application must manually adjust the index with offset
// (e.g. v_indices.size()) when the value is negative(whic means relative // (e.g. v_indices.size()) when the value is negative(relative index).
// index).
// Also, the first index starts with 1, not 0.
// See fixIndex() function in tiny_obj_loader.h for details. // See fixIndex() function in tiny_obj_loader.h for details.
// Also, 0 is set for the index value which // Also, -2147483648(0x80000000) is set for the index value which does not exist in .obj
// does not exist in .obj MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), v_idx, vn_idx, vt_idx);
for (int i = 0; i < num_indices; i++) { if (v_idx != 0x80000000) {
tinyobj::index_t idx = indices[i]; mesh->v_indices.push_back(v_idx);
printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), idx.vertex_index, }
idx.normal_index, idx.texcoord_index); if (vn_idx != 0x80000000) {
mesh->vn_indices.push_back(vn_idx);
if (idx.vertex_index != 0) { }
mesh->v_indices.push_back(idx.vertex_index); if (vt_idx != 0x80000000) {
} mesh->vt_indices.push_back(vt_idx);
if (idx.normal_index != 0) {
mesh->vn_indices.push_back(idx.normal_index);
}
if (idx.texcoord_index != 0) {
mesh->vt_indices.push_back(idx.texcoord_index);
}
} }
} }
void usemtl_cb(void *user_data, const char *name, int material_idx) { void usemtl_cb(void *user_data, const char* name, int material_idx)
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); {
MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
if ((material_idx > -1) && (material_idx < mesh->materials.size())) { if ((material_idx > -1) && (material_idx < mesh->materials.size())) {
printf("usemtl. material id = %d(name = %s)\n", material_idx, printf("usemtl. material id = %d(name = %s)\n", material_idx, mesh->materials[material_idx].name.c_str());
mesh->materials[material_idx].name.c_str());
} else { } else {
printf("usemtl. name = %s\n", name); printf("usemtl. name = %s\n", name);
} }
} }
void mtllib_cb(void *user_data, const tinyobj::material_t *materials, void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_materials)
int num_materials) { {
MyMesh *mesh = reinterpret_cast<MyMesh *>(user_data); MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("mtllib. # of materials = %d\n", num_materials); printf("mtllib. # of materials = %d\n", num_materials);
for (int i = 0; i < num_materials; i++) { for (int i = 0; i < num_materials; i++) {
@@ -103,8 +91,9 @@ void mtllib_cb(void *user_data, const tinyobj::material_t *materials,
} }
} }
void group_cb(void *user_data, const char **names, int num_names) { void group_cb(void *user_data, const char **names, int num_names)
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data); {
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("group : name = \n"); printf("group : name = \n");
for (int i = 0; i < num_names; i++) { for (int i = 0; i < num_names; i++) {
@@ -112,12 +101,16 @@ void group_cb(void *user_data, const char **names, int num_names) {
} }
} }
void object_cb(void *user_data, const char *name) { void object_cb(void *user_data, const char *name)
// MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data); {
//MyMesh *mesh = reinterpret_cast<MyMesh*>(user_data);
printf("object : name = %s\n", name); printf("object : name = %s\n", name);
} }
int main(int argc, char **argv) { int
main(int argc, char** argv)
{
tinyobj::callback_t cb; tinyobj::callback_t cb;
cb.vertex_cb = vertex_cb; cb.vertex_cb = vertex_cb;
cb.normal_cb = normal_cb; cb.normal_cb = normal_cb;
@@ -130,11 +123,7 @@ int main(int argc, char **argv) {
MyMesh mesh; MyMesh mesh;
std::string err; std::string err;
std::string filename = "../../models/cornell_box.obj"; std::ifstream ifs("../../models/cornell_box.obj");
if (argc > 1) {
filename = std::string(argv[1]);
}
std::ifstream ifs(filename.c_str());
if (ifs.fail()) { if (ifs.fail()) {
std::cerr << "file not found." << std::endl; std::cerr << "file not found." << std::endl;
@@ -142,8 +131,8 @@ int main(int argc, char **argv) {
} }
tinyobj::MaterialFileReader mtlReader("../../models/"); tinyobj::MaterialFileReader mtlReader("../../models/");
bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err); bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader);
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,15 @@
// //
// Simple .obj viewer(vertex only) // Simple .obj viewer(vertex only)
// //
#include <algorithm> #include <vector>
#include <cassert> #include <string>
#include <cmath>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <limits> #include <limits>
#include <map> #include <cmath>
#include <string> #include <cassert>
#include <vector> #include <algorithm>
#include <GL/glew.h> #include <GL/glew.h>
@@ -27,23 +26,11 @@
#include "trackball.h" #include "trackball.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#ifdef _WIN32 #ifdef _WIN32
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <windows.h> #include <windows.h>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#include <mmsystem.h> #include <mmsystem.h>
#ifdef __cplusplus #ifdef __cplusplus
} }
@@ -58,7 +45,7 @@ extern "C" {
#endif #endif
class timerutil { class timerutil {
public: public:
#ifdef _WIN32 #ifdef _WIN32
typedef DWORD time_t; typedef DWORD time_t;
@@ -94,7 +81,7 @@ class timerutil {
return (time_t)(t.tv_sec * 1000 + t.tv_usec); return (time_t)(t.tv_sec * 1000 + t.tv_usec);
} }
#else // C timer #else // C timer
// using namespace std; // using namespace std;
typedef clock_t time_t; typedef clock_t time_t;
@@ -109,7 +96,7 @@ class timerutil {
#endif #endif
#endif #endif
private: private:
#ifdef _WIN32 #ifdef _WIN32
DWORD t_[2]; DWORD t_[2];
#else #else
@@ -123,9 +110,8 @@ class timerutil {
}; };
typedef struct { typedef struct {
GLuint vb; // vertex buffer GLuint vb; // vertex buffer
int numTriangles; int numTriangles;
size_t material_id;
} DrawObject; } DrawObject;
std::vector<DrawObject> gDrawObjects; std::vector<DrawObject> gDrawObjects;
@@ -143,26 +129,7 @@ float eye[3], lookat[3], up[3];
GLFWwindow* window; GLFWwindow* window;
static std::string GetBaseDir(const std::string &filepath) { void CheckErrors(std::string desc) {
if (filepath.find_last_of("/\\") != std::string::npos)
return filepath.substr(0, filepath.find_last_of("/\\"));
return "";
}
static bool FileExists(const std::string &abs_filename) {
bool ret;
FILE *fp = fopen(abs_filename.c_str(), "rb");
if (fp) {
ret = true;
fclose(fp);
} else {
ret = false;
}
return ret;
}
static void CheckErrors(std::string desc) {
GLenum e = glGetError(); GLenum e = glGetError();
if (e != GL_NO_ERROR) { if (e != GL_NO_ERROR) {
fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e); fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e);
@@ -170,7 +137,7 @@ static void CheckErrors(std::string desc) {
} }
} }
static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) { void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
float v10[3]; float v10[3];
v10[0] = v1[0] - v0[0]; v10[0] = v1[0] - v0[0];
v10[1] = v1[1] - v0[1]; v10[1] = v1[1] - v0[1];
@@ -188,46 +155,36 @@ static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2]; float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2];
if (len2 > 0.0f) { if (len2 > 0.0f) {
float len = sqrtf(len2); float len = sqrtf(len2);
N[0] /= len; N[0] /= len;
N[1] /= len; N[1] /= len;
} }
} }
static bool LoadObjAndConvert(float bmin[3], float bmax[3], bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector<DrawObject>& drawObjects, const char* filename)
std::vector<DrawObject>* drawObjects, {
std::vector<tinyobj::material_t>& materials,
std::map<std::string, GLuint>& textures,
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;
timerutil tm; timerutil tm;
tm.start(); tm.start();
std::string base_dir = GetBaseDir(filename);
#ifdef _WIN32
base_dir += "\\";
#else
base_dir += "/";
#endif
std::string err; std::string err;
bool ret = bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL);
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, base_dir.c_str());
if (!err.empty()) { if (!err.empty()) {
std::cerr << err << std::endl; std::cerr << err << std::endl;
} }
tm.end(); tm.end();
if (!ret) { if (!ret) {
std::cerr << "Failed to load " << filename << std::endl; std::cerr << "Failed to load " << filename << std::endl;
return false; return false;
} }
printf("Parsing time: %d [ms]\n", (int)tm.msec()); printf("Parsing time: %d [ms]\n", tm.msec());
printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3); printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3);
printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3); printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3);
@@ -235,204 +192,106 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3],
printf("# of materials = %d\n", (int)materials.size()); printf("# of materials = %d\n", (int)materials.size());
printf("# of shapes = %d\n", (int)shapes.size()); printf("# of shapes = %d\n", (int)shapes.size());
// Append `default` material
materials.push_back(tinyobj::material_t());
// Load diffuse textures
{
for (size_t m = 0; m < materials.size(); m++) {
tinyobj::material_t* mp = &materials[m];
if (mp->diffuse_texname.length() > 0) {
// Only load the texture if it is not already loaded
if (textures.find(mp->diffuse_texname) == textures.end()) {
GLuint texture_id;
int w, h;
int comp;
std::string texture_filename = mp->diffuse_texname;
if (!FileExists(texture_filename)) {
// Append base dir.
texture_filename = base_dir + mp->diffuse_texname;
if (!FileExists(texture_filename)) {
std::cerr << "Unable to find file: " << mp->diffuse_texname << std::endl;
exit(1);
}
}
unsigned char* image = stbi_load(texture_filename.c_str(), &w, &h, &comp, STBI_default);
if (image == nullptr) {
std::cerr << "Unable to load texture: " << texture_filename << std::endl;
exit(1);
}
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (comp == 3) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
}
else if (comp == 4) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
}
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
textures.insert(std::make_pair(mp->diffuse_texname, texture_id));
}
}
}
}
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();
{ {
for (size_t s = 0; s < shapes.size(); s++) { for (size_t s = 0; s < shapes.size(); s++) {
DrawObject o; DrawObject o;
std::vector<float> vb; // pos(3float), normal(3float), color(3float) std::vector<float> vb; // pos(3float), normal(3float), color(3float)
for (size_t f = 0; f < shapes[s].mesh.indices.size() / 3; f++) { for (size_t f = 0; f < shapes[s].mesh.indices.size()/3; f++) {
tinyobj::index_t idx0 = shapes[s].mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shapes[s].mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shapes[s].mesh.indices[3 * f + 2];
int current_material_id = shapes[s].mesh.material_ids[f];
if ((current_material_id < 0) || (current_material_id >= static_cast<int>(materials.size()))) { tinyobj::index_t idx0 = shapes[s].mesh.indices[3*f+0];
// Invaid material ID. Use default material. tinyobj::index_t idx1 = shapes[s].mesh.indices[3*f+1];
current_material_id = materials.size() - 1; // Default material is added to the last item in `materials`. tinyobj::index_t idx2 = shapes[s].mesh.indices[3*f+2];
}
//if (current_material_id >= materials.size()) {
// std::cerr << "Invalid material index: " << current_material_id << std::endl;
//}
//
float diffuse[3];
for (size_t i = 0; i < 3; i++) {
diffuse[i] = materials[current_material_id].diffuse[i];
}
float tc[3][2];
if (attrib.texcoords.size() > 0) {
assert(attrib.texcoords.size() > 2 * idx0.texcoord_index + 1);
assert(attrib.texcoords.size() > 2 * idx1.texcoord_index + 1);
assert(attrib.texcoords.size() > 2 * idx2.texcoord_index + 1);
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
tc[0][1] = 1.0f - attrib.texcoords[2 * idx0.texcoord_index + 1];
tc[1][0] = attrib.texcoords[2 * idx1.texcoord_index];
tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1];
tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index];
tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1];
} else {
std::cerr << "Texcoordinates are not defined" << std::endl;
exit(2);
}
float v[3][3]; float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3 * f0 + k];
v[1][k] = attrib.vertices[3 * f1 + k];
v[2][k] = attrib.vertices[3 * f2 + k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
float n[3][3];
if (attrib.normals.size() > 0) {
int f0 = idx0.normal_index;
int f1 = idx1.normal_index;
int f2 = idx2.normal_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3 * f0 + k]; int f0 = idx0.vertex_index;
n[1][k] = attrib.normals[3 * f1 + k]; int f1 = idx1.vertex_index;
n[2][k] = attrib.normals[3 * f2 + k]; int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3*f0+k];
v[1][k] = attrib.vertices[3*f1+k];
v[2][k] = attrib.vertices[3*f2+k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
} }
} else {
// compute geometric normal float n[3][3];
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0]; if (attrib.normals.size() > 0) {
n[1][1] = n[0][1]; int f0 = idx0.normal_index;
n[1][2] = n[0][2]; int f1 = idx1.normal_index;
n[2][0] = n[0][0]; int f2 = idx2.normal_index;
n[2][1] = n[0][1]; assert(f0 >= 0);
n[2][2] = n[0][2]; assert(f1 >= 0);
assert(f2 >= 0);
for (int k = 0; k < 3; k++) {
n[0][k] = attrib.normals[3*f0+k];
n[1][k] = attrib.normals[3*f1+k];
n[2][k] = attrib.normals[3*f2+k];
}
} else {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2];
n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2];
}
for (int k = 0; k < 3; k++) {
vb.push_back(v[k][0]);
vb.push_back(v[k][1]);
vb.push_back(v[k][2]);
vb.push_back(n[k][0]);
vb.push_back(n[k][1]);
vb.push_back(n[k][2]);
// Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb.push_back(c[0] * 0.5 + 0.5);
vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5);
}
} }
for (int k = 0; k < 3; k++) { o.vb = 0;
vb.push_back(v[k][0]); o.numTriangles = 0;
vb.push_back(v[k][1]); if (vb.size() > 0) {
vb.push_back(v[k][2]); glGenBuffers(1, &o.vb);
vb.push_back(n[k][0]); glBindBuffer(GL_ARRAY_BUFFER, o.vb);
vb.push_back(n[k][1]); glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW);
vb.push_back(n[k][2]); o.numTriangles = vb.size() / 9 / 3;
// Combine normal and diffuse to get color. printf("shape[%d] # of triangles = %d\n", static_cast<int>(s), o.numTriangles);
float normal_factor = 0.2;
float diffuse_factor = 1 - normal_factor;
float c[3] = {
n[k][0] * normal_factor + diffuse[0] * diffuse_factor,
n[k][1] * normal_factor + diffuse[1] * diffuse_factor,
n[k][2] * normal_factor + diffuse[2] * diffuse_factor
};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
c[0] /= len;
c[1] /= len;
c[2] /= len;
}
vb.push_back(c[0] * 0.5 + 0.5);
vb.push_back(c[1] * 0.5 + 0.5);
vb.push_back(c[2] * 0.5 + 0.5);
vb.push_back(tc[k][0]);
vb.push_back(tc[k][1]);
} }
}
o.vb = 0; gDrawObjects.push_back(o);
o.numTriangles = 0;
// OpenGL viewer does not support texturing with per-face material.
if (shapes[s].mesh.material_ids.size() > 0 && shapes[s].mesh.material_ids.size() > s) {
// Base case
o.material_id = shapes[s].mesh.material_ids[s];
} else {
o.material_id = materials.size() - 1; // = ID for default material.
}
if (vb.size() > 0) {
glGenBuffers(1, &o.vb);
glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0),
GL_STATIC_DRAW);
o.numTriangles = vb.size() / (3 + 3 + 3 + 2) * 3;
printf("shape[%d] # of triangles = %d\n", static_cast<int>(s),
o.numTriangles);
}
drawObjects->push_back(o);
} }
} }
printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
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;
} }
static 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);
@@ -448,122 +307,100 @@ static void reshapeFunc(GLFWwindow* window, int w, int h) {
height = h; height = h;
} }
static void keyboardFunc(GLFWwindow* window, int key, int scancode, int action, void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) {
int mods) { if(action == GLFW_PRESS || action == GLFW_REPEAT){
(void)window; // Move camera
(void)scancode; float mv_x = 0, mv_y = 0, mv_z = 0;
(void)mods; if(key == GLFW_KEY_K) mv_x += 1;
if (action == GLFW_PRESS || action == GLFW_REPEAT) { else if(key == GLFW_KEY_J) mv_x += -1;
// Move camera else if(key == GLFW_KEY_L) mv_y += 1;
float mv_x = 0, mv_y = 0, mv_z = 0; else if(key == GLFW_KEY_H) mv_y += -1;
if (key == GLFW_KEY_K) else if(key == GLFW_KEY_P) mv_z += 1;
mv_x += 1; else if(key == GLFW_KEY_N) mv_z += -1;
else if (key == GLFW_KEY_J) //camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
mv_x += -1; // Close window
else if (key == GLFW_KEY_L) if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE);
mv_y += 1;
else if (key == GLFW_KEY_H)
mv_y += -1;
else if (key == GLFW_KEY_P)
mv_z += 1;
else if (key == GLFW_KEY_N)
mv_z += -1;
// camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05);
// Close window
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE)
glfwSetWindowShouldClose(window, GL_TRUE);
// init_frame = true; //init_frame = true;
} }
} }
static void clickFunc(GLFWwindow* window, int button, int action, int mods) { void clickFunc(GLFWwindow* window, int button, int action, int mods){
(void)window; if(button == GLFW_MOUSE_BUTTON_LEFT){
(void)mods; if(action == GLFW_PRESS){
if (button == GLFW_MOUSE_BUTTON_LEFT) { mouseLeftPressed = true;
if (action == GLFW_PRESS) { trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
mouseLeftPressed = true; } else if(action == GLFW_RELEASE){
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); mouseLeftPressed = false;
} else if (action == GLFW_RELEASE) { }
mouseLeftPressed = false;
} }
} if(button == GLFW_MOUSE_BUTTON_RIGHT){
if (button == GLFW_MOUSE_BUTTON_RIGHT) { if(action == GLFW_PRESS){
if (action == GLFW_PRESS) { mouseRightPressed = true;
mouseRightPressed = true; } else if(action == GLFW_RELEASE){
} else if (action == GLFW_RELEASE) { mouseRightPressed = false;
mouseRightPressed = false; }
} }
} if(button == GLFW_MOUSE_BUTTON_MIDDLE){
if (button == GLFW_MOUSE_BUTTON_MIDDLE) { if(action == GLFW_PRESS){
if (action == GLFW_PRESS) { mouseMiddlePressed = true;
mouseMiddlePressed = true; } else if(action == GLFW_RELEASE){
} else if (action == GLFW_RELEASE) { mouseMiddlePressed = false;
mouseMiddlePressed = false; }
} }
}
} }
static void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y) { void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){
(void)window;
float rotScale = 1.0f; float rotScale = 1.0f;
float transScale = 2.0f; float transScale = 2.0f;
if (mouseLeftPressed) { if(mouseLeftPressed){
trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width, trackball(prev_quat,
rotScale * (height - 2.0f * prevMouseY) / (float)height, rotScale * (2.0f * prevMouseX - width) / (float)width,
rotScale * (2.0f * mouse_x - width) / (float)width, rotScale * (height - 2.0f * prevMouseY) / (float)height,
rotScale * (height - 2.0f * mouse_y) / (float)height); rotScale * (2.0f * mouse_x - width) / (float)width,
rotScale * (height - 2.0f * mouse_y) / (float)height);
add_quats(prev_quat, curr_quat, curr_quat); add_quats(prev_quat, curr_quat, curr_quat);
} else if (mouseMiddlePressed) { } else if (mouseMiddlePressed) {
eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width; eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width; lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width;
eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; eye[1] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height;
} else if (mouseRightPressed) { } else if (mouseRightPressed) {
eye[2] += transScale * (mouse_y - prevMouseY) / (float)height; eye[2] += transScale * (mouse_y - prevMouseY) / (float)height;
lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height; lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height;
} }
// Update mouse point // Update mouse point
prevMouseX = mouse_x; prevMouseX = mouse_x;
prevMouseY = mouse_y; prevMouseY = mouse_y;
} }
static void Draw(const std::vector<DrawObject>& drawObjects, std::vector<tinyobj::material_t>& materials, std::map<std::string, GLuint>& textures) { 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);
glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0); glPolygonOffset(1.0, 1.0);
GLsizei stride = (3 + 3 + 3 + 2) * sizeof(float); glColor3f(1.0f, 1.0f, 1.0f);
for (size_t i = 0; i < drawObjects.size(); i++) { for (size_t i = 0; i < drawObjects.size(); i++) {
DrawObject o = drawObjects[i]; DrawObject o = drawObjects[i];
if (o.vb < 1) { if (o.vb < 1) {
continue; continue;
} }
glBindBuffer(GL_ARRAY_BUFFER, o.vb); glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(3, GL_FLOAT, 36, (const void*)0);
glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3));
if ((o.material_id < materials.size())) { glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6));
std::string diffuse_texname = materials[o.material_id].diffuse_texname;
if (textures.find(diffuse_texname) != textures.end()) {
glBindTexture(GL_TEXTURE_2D, textures[diffuse_texname]);
}
}
glVertexPointer(3, GL_FLOAT, stride, (const void*)0);
glNormalPointer(GL_FLOAT, stride, (const void*)(sizeof(float) * 3));
glColorPointer(3, GL_FLOAT, stride, (const void*)(sizeof(float) * 6));
glTexCoordPointer(2, GL_FLOAT, stride, (const void*)(sizeof(float) * 9));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays"); CheckErrors("drawarrays");
glBindTexture(GL_TEXTURE_2D, 0);
} }
// draw wireframe // draw wireframe
@@ -577,16 +414,13 @@ static void Draw(const std::vector<DrawObject>& drawObjects, std::vector<tinyobj
if (o.vb < 1) { if (o.vb < 1) {
continue; continue;
} }
glBindBuffer(GL_ARRAY_BUFFER, o.vb); glBindBuffer(GL_ARRAY_BUFFER, o.vb);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(3, GL_FLOAT, 36, (const void*)0);
glVertexPointer(3, GL_FLOAT, stride, (const void*)0); glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3));
glNormalPointer(GL_FLOAT, stride, (const void*)(sizeof(float) * 3));
glColorPointer(3, GL_FLOAT, stride, (const void*)(sizeof(float) * 6));
glTexCoordPointer(2, GL_FLOAT, stride, (const void*)(sizeof(float) * 9));
glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles);
CheckErrors("drawarrays"); CheckErrors("drawarrays");
@@ -609,21 +443,26 @@ 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;
} }
Init(); Init();
if (!glfwInit()) { if(!glfwInit()){
std::cerr << "Failed to initialize GLFW." << std::endl; std::cerr << "Failed to initialize GLFW." << std::endl;
return -1; return -1;
} }
window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL); window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL);
if (window == NULL) { if(window == NULL){
std::cerr << "Failed to open GLFW window. " << std::endl; std::cerr << "Failed to open GLFW window. " << std::endl;
glfwTerminate(); glfwTerminate();
return 1; return 1;
@@ -647,9 +486,7 @@ int main(int argc, char** argv) {
reshapeFunc(window, width, height); reshapeFunc(window, width, height);
float bmin[3], bmax[3]; float bmin[3], bmax[3];
std::vector<tinyobj::material_t> materials; if (false == LoadObjAndConvert(bmin, bmax, gDrawObjects, argv[1])) {
std::map<std::string, GLuint> textures;
if (false == LoadObjAndConvert(bmin, bmax, &gDrawObjects, materials, textures, argv[1])) {
return -1; return -1;
} }
@@ -661,20 +498,18 @@ int main(int argc, char** argv) {
maxExtent = 0.5f * (bmax[2] - bmin[2]); maxExtent = 0.5f * (bmax[2] - bmin[2]);
} }
while (glfwWindowShouldClose(window) == GL_FALSE) { while(glfwWindowShouldClose(window) == GL_FALSE) {
glfwPollEvents(); glfwPollEvents();
glClearColor(0.1f, 0.2f, 0.3f, 1.0f); glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
// camera & rotate // camera & rotate
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
GLfloat mat[4][4]; GLfloat mat[4][4];
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]);
up[1], up[2]);
build_rotmatrix(mat, curr_quat); build_rotmatrix(mat, curr_quat);
glMultMatrixf(&mat[0][0]); glMultMatrixf(&mat[0][0]);
@@ -682,10 +517,9 @@ int main(int argc, char** argv) {
glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent); glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent);
// Centerize object. // Centerize object.
glTranslatef(-0.5 * (bmax[0] + bmin[0]), -0.5 * (bmax[1] + bmin[1]), glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2]));
-0.5 * (bmax[2] + bmin[2]));
Draw(gDrawObjects);
Draw(gDrawObjects, materials, textures);
glfwSwapBuffers(window); glfwSwapBuffers(window);
} }

View File

@@ -1,74 +0,0 @@
#define VOXELIZER_IMPLEMENTATION
#include "voxelizer.h"
#define TINYOBJLOADER_IMPLEMENTATION
#include "../../tiny_obj_loader.h"
bool Voxelize(const char* filename, float voxelsizex, float voxelsizey, float voxelsizez, float precision)
{
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, filename);
if (!err.empty()) {
printf("err: %s\n", err.c_str());
}
if (!ret) {
printf("failed to load : %s\n", filename);
return false;
}
if (shapes.size() == 0) {
printf("err: # of shapes are zero.\n");
return false;
}
// Only use first shape.
{
vx_mesh_t* mesh;
vx_mesh_t* result;
mesh = vx_mesh_alloc(attrib.vertices.size(), shapes[0].mesh.indices.size());
for (size_t f = 0; f < shapes[0].mesh.indices.size(); f++) {
mesh->indices[f] = shapes[0].mesh.indices[f].vertex_index;
}
for (size_t v = 0; v < attrib.vertices.size() / 3; v++) {
mesh->vertices[v].x = attrib.vertices[3*v+0];
mesh->vertices[v].y = attrib.vertices[3*v+1];
mesh->vertices[v].z = attrib.vertices[3*v+2];
}
result = vx_voxelize(mesh, voxelsizex, voxelsizey, voxelsizez, precision);
printf("Number of vertices: %ld\n", result->nvertices);
printf("Number of indices: %ld\n", result->nindices);
}
return true;
}
int
main(
int argc,
char** argv)
{
if (argc < 4) {
printf("Usage: voxelize input.obj voxelsizex voxelsizey voxelsizez precision\n");
exit(-1);
}
const char* filename = argv[1];
float voxelsizex = atof(argv[2]);
float voxelsizey = atof(argv[3]);
float voxelsizez = atof(argv[4]);
float prec = atof(argv[5]);
bool ret = Voxelize(filename, voxelsizex, voxelsizey, voxelsizez, prec);
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}

12
experimental/Makefile Normal file
View File

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

View File

@@ -1,5 +0,0 @@
Experimental code for .obj loader.
* Multi-threaded optimized parser : tinyobj_loader_opt.h
* zstd compressed .obj support. `--with-zstd` premake option.
* gzip compressed .obj support. `--with-zlib` premake option.

View File

@@ -3,11 +3,6 @@ newoption {
description = "Build with zlib." description = "Build with zlib."
} }
newoption {
trigger = "with-zstd",
description = "Build with ZStandard compression."
}
solution "objview" solution "objview"
-- location ( "build" ) -- location ( "build" )
configurations { "Release", "Debug" } configurations { "Release", "Debug" }
@@ -28,15 +23,6 @@ solution "objview"
links { 'z' } links { 'z' }
end end
if _OPTIONS['with-zstd'] then
print("with-zstd")
defines { 'ENABLE_ZSTD' }
-- Set path to zstd installed dir.
includedirs { '$$HOME/local/include' }
libdirs { '$$HOME/local/lib' }
links { 'zstd' }
end
-- Uncomment if you want address sanitizer(gcc/clang only) -- Uncomment if you want address sanitizer(gcc/clang only)
--buildoptions { "-fsanitize=address" } --buildoptions { "-fsanitize=address" }
--linkoptions { "-fsanitize=address" } --linkoptions { "-fsanitize=address" }

File diff suppressed because it is too large Load Diff

View File

@@ -292,22 +292,6 @@ typedef struct {
std::string bump_texname; // map_bump, bump std::string bump_texname; // map_bump, bump
std::string displacement_texname; // disp std::string displacement_texname; // disp
std::string alpha_texname; // map_d std::string alpha_texname; // map_d
// PBR extension
// http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
float roughness; // [0, 1] default 0
float metallic; // [0, 1] default 0
float sheen; // [0, 1] default 0
float clearcoat_thickness; // [0, 1] default 0
float clearcoat_roughness; // [0, 1] default 0
float anisotropy; // aniso. [0, 1] default 0
float anisotropy_rotation; // anisor. [0, 1] default 0
std::string roughness_texname; // map_Pr
std::string metallic_texname; // map_Pm
std::string sheen_texname; // map_Ps
std::string emissive_texname; // map_Ke
std::string normal_texname; // norm. For normal mapping.
std::map<std::string, std::string> unknown_parameter; std::map<std::string, std::string> unknown_parameter;
} material_t; } material_t;
@@ -317,20 +301,19 @@ typedef struct {
unsigned int length; unsigned int length;
} shape_t; } shape_t;
struct index_t { struct vertex_index {
int vertex_index, texcoord_index, normal_index; int v_idx, vt_idx, vn_idx;
index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {} vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
explicit index_t(int idx) explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
: vertex_index(idx), texcoord_index(idx), normal_index(idx) {} vertex_index(int vidx, int vtidx, int vnidx)
index_t(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
: vertex_index(vidx), texcoord_index(vtidx), normal_index(vnidx) {}
}; };
typedef struct { typedef struct {
std::vector<float, lt::allocator<float> > vertices; std::vector<float, lt::allocator<float> > vertices;
std::vector<float, lt::allocator<float> > normals; std::vector<float, lt::allocator<float> > normals;
std::vector<float, lt::allocator<float> > texcoords; std::vector<float, lt::allocator<float> > texcoords;
std::vector<index_t, lt::allocator<index_t> > indices; std::vector<vertex_index, lt::allocator<vertex_index> > faces;
std::vector<int, lt::allocator<int> > face_num_verts; std::vector<int, lt::allocator<int> > face_num_verts;
std::vector<int, lt::allocator<int> > material_ids; std::vector<int, lt::allocator<int> > material_ids;
} attrib_t; } attrib_t;
@@ -403,11 +386,12 @@ static inline int fixIndex(int idx, int n) {
} }
// Parse raw triples: i, i/j/k, i//k, i/j // Parse raw triples: i, i/j/k, i//k, i/j
static index_t parseRawTriple(const char **token) { static vertex_index parseRawTriple(const char **token) {
index_t vi( vertex_index vi(
static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid static_cast<int>(0x80000000)); // 0x80000000 = -2147483648 = invalid
vi.vertex_index = my_atoi((*token)); vi.v_idx = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -420,7 +404,8 @@ static index_t parseRawTriple(const char **token) {
// i//k // i//k
if ((*token)[0] == '/') { if ((*token)[0] == '/') {
(*token)++; (*token)++;
vi.normal_index = my_atoi((*token)); vi.vn_idx = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -429,7 +414,8 @@ static index_t parseRawTriple(const char **token) {
} }
// i/j/k or i/j // i/j/k or i/j
vi.texcoord_index = my_atoi((*token)); vi.vt_idx = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -440,7 +426,8 @@ static index_t parseRawTriple(const char **token) {
// i/j/k // i/j/k
(*token)++; // skip '/' (*token)++; // skip '/'
vi.normal_index = my_atoi((*token)); vi.vn_idx = my_atoi((*token));
//(*token) += strcspn((*token), "/ \t\r");
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
(*token)[0] != '\t' && (*token)[0] != '\r') { (*token)[0] != '\t' && (*token)[0] != '\r') {
(*token)++; (*token)++;
@@ -449,17 +436,17 @@ static index_t parseRawTriple(const char **token) {
} }
static inline bool parseString(ShortString *s, const char **token) { static inline bool parseString(ShortString *s, const char **token) {
skip_space(token); skip_space(token); //(*token) += strspn((*token), " \t");
size_t e = until_space((*token)); size_t e = until_space((*token)); // strcspn((*token), " \t\r");
(*s)->insert((*s)->end(), (*token), (*token) + e); (*s)->insert((*s)->end(), (*token), (*token) + e);
(*token) += e; (*token) += e;
return true; return true;
} }
static inline int parseInt(const char **token) { static inline int parseInt(const char **token) {
skip_space(token); skip_space(token); //(*token) += strspn((*token), " \t");
int i = my_atoi((*token)); int i = my_atoi((*token));
(*token) += until_space((*token)); (*token) += until_space((*token)); // strcspn((*token), " \t\r");
return i; return i;
} }
@@ -598,9 +585,9 @@ assemble :
{ {
// = pow(5.0, exponent); // = pow(5.0, exponent);
double a = 1.0; double a = 5.0;
for (int i = 0; i < exponent; i++) { for (int i = 0; i < exponent; i++) {
a = a * 5.0; a = a * a;
} }
*result = *result =
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); //(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
@@ -614,12 +601,13 @@ fail:
} }
static inline float parseFloat(const char **token) { static inline float parseFloat(const char **token) {
skip_space(token); skip_space(token); //(*token) += strspn((*token), " \t");
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER #ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
float f = static_cast<float>(atof(*token)); float f = static_cast<float>(atof(*token));
(*token) += strcspn((*token), " \t\r"); (*token) += strcspn((*token), " \t\r");
#else #else
const char *end = (*token) + until_space((*token)); const char *end =
(*token) + until_space((*token)); // strcspn((*token), " \t\r");
double val = 0.0; double val = 0.0;
tryParseDouble((*token), end, &val); tryParseDouble((*token), end, &val);
float f = static_cast<float>(val); float f = static_cast<float>(val);
@@ -677,11 +665,6 @@ static void LoadMtl(std::map<std::string, int> *material_map,
std::string linebuf(&buf[0]); std::string linebuf(&buf[0]);
// Trim trailing whitespace.
if (linebuf.size() > 0) {
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
}
// Trim newline '\r\n' or '\n' // Trim newline '\r\n' or '\n'
if (linebuf.size() > 0) { if (linebuf.size() > 0) {
if (linebuf[linebuf.size() - 1] == '\n') if (linebuf[linebuf.size() - 1] == '\n')
@@ -764,8 +747,7 @@ static void LoadMtl(std::map<std::string, int> *material_map,
} }
// transmittance // transmittance
if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) {
(token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
token += 2; token += 2;
float r, g, b; float r, g, b;
parseFloat3(&r, &g, &b, &token); parseFloat3(&r, &g, &b, &token);
@@ -813,7 +795,6 @@ static void LoadMtl(std::map<std::string, int> *material_map,
material.dissolve = parseFloat(&token); material.dissolve = parseFloat(&token);
continue; continue;
} }
if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2; token += 2;
// Invert value of Tr(assume Tr is in range [0, 1]) // Invert value of Tr(assume Tr is in range [0, 1])
@@ -821,55 +802,6 @@ static void LoadMtl(std::map<std::string, int> *material_map,
continue; continue;
} }
// PBR: roughness
if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) {
token += 2;
material.roughness = parseFloat(&token);
continue;
}
// PBR: metallic
if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) {
token += 2;
material.metallic = parseFloat(&token);
continue;
}
// PBR: sheen
if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) {
token += 2;
material.sheen = parseFloat(&token);
continue;
}
// PBR: clearcoat thickness
if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) {
token += 2;
material.clearcoat_thickness = parseFloat(&token);
continue;
}
// PBR: clearcoat roughness
if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) {
token += 4;
material.clearcoat_roughness = parseFloat(&token);
continue;
}
// PBR: anisotropy
if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) {
token += 6;
material.anisotropy = parseFloat(&token);
continue;
}
// PBR: anisotropy rotation
if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) {
token += 7;
material.anisotropy_rotation = parseFloat(&token);
continue;
}
// ambient texture // ambient texture
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
@@ -926,41 +858,6 @@ static void LoadMtl(std::map<std::string, int> *material_map,
continue; continue;
} }
// PBR: roughness texture
if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
token += 7;
material.roughness_texname = token;
continue;
}
// PBR: metallic texture
if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
token += 7;
material.metallic_texname = token;
continue;
}
// PBR: sheen texture
if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
token += 7;
material.sheen_texname = token;
continue;
}
// PBR: emissive texture
if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
token += 7;
material.emissive_texname = token;
continue;
}
// PBR: normal map texture
if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
token += 5;
material.normal_texname = token;
continue;
}
// unknown parameter // unknown parameter
const char *_space = strchr(token, ' '); const char *_space = strchr(token, ' ');
if (!_space) { if (!_space) {
@@ -999,8 +896,8 @@ typedef struct {
float tx, ty; float tx, ty;
// for f // for f
std::vector<index_t, lt::allocator<index_t> > f; std::vector<vertex_index, lt::allocator<vertex_index> > f;
// std::vector<index_t> f; // std::vector<vertex_index> f;
std::vector<int, lt::allocator<int> > f_num_verts; std::vector<int, lt::allocator<int> > f_num_verts;
const char *group_name; const char *group_name;
@@ -1021,42 +918,44 @@ struct CommandCount {
size_t num_vn; size_t num_vn;
size_t num_vt; size_t num_vt;
size_t num_f; size_t num_f;
size_t num_indices; size_t num_faces;
CommandCount() { CommandCount() {
num_v = 0; num_v = 0;
num_vn = 0; num_vn = 0;
num_vt = 0; num_vt = 0;
num_f = 0; num_f = 0;
num_indices = 0; num_faces = 0;
} }
}; };
class LoadOption { class
LoadOption
{
public: public:
LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {} LoadOption() : req_num_threads(-1), triangulate(true), verbose(false) {}
int req_num_threads;
bool triangulate;
bool verbose;
int req_num_threads;
bool triangulate;
bool verbose;
}; };
/// Parse wavefront .obj(.obj string data is expanded to linear char array /// Parse wavefront .obj(.obj string data is expanded to linear char array
/// `buf') /// `buf')
/// -1 to req_num_threads use the number of HW threads in the running system. /// -1 to req_num_threads use the number of HW threads in the running system.
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
std::vector<material_t> *materials, const char *buf, size_t len, size_t len, const LoadOption& option);
const LoadOption &option);
#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION #ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
static bool parseLine(Command *command, const char *p, size_t p_len, static bool parseLine(Command *command, const char *p, size_t p_len,
bool triangulate = true) { bool triangulate = true) {
// @todo { operate directly on pointer `p'. to do that, add range check for
// string operatoion against `p', since `p' is not null-terminated at p[p_len]
// }
char linebuf[4096]; char linebuf[4096];
assert(p_len < 4095); assert(p_len < 4095);
memcpy(linebuf, p, p_len); // StackVector<char, 256> linebuf;
// linebuf->resize(p_len + 1);
memcpy(&linebuf, p, p_len);
linebuf[p_len] = '\0'; linebuf[p_len] = '\0';
const char *token = linebuf; const char *token = linebuf;
@@ -1064,7 +963,8 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
command->type = COMMAND_EMPTY; command->type = COMMAND_EMPTY;
// Skip leading space. // Skip leading space.
skip_space(&token); // token += strspn(token, " \t");
skip_space(&token); //(*token) += strspn((*token), " \t");
assert(token); assert(token);
if (token[0] == '\0') { // empty line if (token[0] == '\0') { // empty line
@@ -1078,7 +978,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// vertex // vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) { if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2; token += 2;
float x = 0.0f, y = 0.0f, z = 0.0f; float x, y, z;
parseFloat3(&x, &y, &z, &token); parseFloat3(&x, &y, &z, &token);
command->vx = x; command->vx = x;
command->vy = y; command->vy = y;
@@ -1090,7 +990,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// normal // normal
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
token += 3; token += 3;
float x = 0.0f, y = 0.0f, z = 0.0f; float x, y, z;
parseFloat3(&x, &y, &z, &token); parseFloat3(&x, &y, &z, &token);
command->nx = x; command->nx = x;
command->ny = y; command->ny = y;
@@ -1102,7 +1002,7 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// texcoord // texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3; token += 3;
float x = 0.0f, y = 0.0f; float x, y;
parseFloat2(&x, &y, &token); parseFloat2(&x, &y, &token);
command->tx = x; command->tx = x;
command->ty = y; command->ty = y;
@@ -1113,12 +1013,19 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
// face // face
if (token[0] == 'f' && IS_SPACE((token[1]))) { if (token[0] == 'f' && IS_SPACE((token[1]))) {
token += 2; token += 2;
// token += strspn(token, " \t");
skip_space(&token); skip_space(&token);
StackVector<index_t, 8> f; StackVector<vertex_index, 8> f;
while (!IS_NEW_LINE(token[0])) { while (!IS_NEW_LINE(token[0])) {
index_t vi = parseRawTriple(&token); vertex_index vi = parseRawTriple(&token);
// printf("v = %d, %d, %d\n", vi.v_idx, vi.vn_idx, vi.vt_idx);
// if (callback.index_cb) {
// callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx);
//}
// size_t n = strspn(token, " \t\r");
// token += n;
skip_space_and_cr(&token); skip_space_and_cr(&token);
f->push_back(vi); f->push_back(vi);
@@ -1127,9 +1034,9 @@ static bool parseLine(Command *command, const char *p, size_t p_len,
command->type = COMMAND_F; command->type = COMMAND_F;
if (triangulate) { if (triangulate) {
index_t i0 = f[0]; vertex_index i0 = f[0];
index_t i1(-1); vertex_index i1(-1);
index_t i2 = f[1]; vertex_index i2 = f[1];
for (size_t k = 2; k < f->size(); k++) { for (size_t k = 2; k < f->size(); k++) {
i1 = i2; i1 = i2;
@@ -1246,28 +1153,27 @@ static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
return false; return false;
} }
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
std::vector<material_t> *materials, const char *buf, size_t len, size_t len, const LoadOption& option)
const LoadOption &option) { {
attrib->vertices.clear(); attrib->vertices.clear();
attrib->normals.clear(); attrib->normals.clear();
attrib->texcoords.clear(); attrib->texcoords.clear();
attrib->indices.clear(); attrib->faces.clear();
attrib->face_num_verts.clear(); attrib->face_num_verts.clear();
attrib->material_ids.clear(); attrib->material_ids.clear();
shapes->clear(); shapes->clear();
if (len < 1) return false; if (len < 1) return false;
auto num_threads = (option.req_num_threads < 0) auto num_threads = (option.req_num_threads < 0) ? std::thread::hardware_concurrency()
? std::thread::hardware_concurrency() : option.req_num_threads;
: option.req_num_threads;
num_threads = num_threads =
std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads)); std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
if (option.verbose) { if (option.verbose) {
std::cout << "# of threads = " << num_threads << std::endl; std::cout << "# of threads = " << num_threads << std::endl;
} }
auto t1 = std::chrono::high_resolution_clock::now(); auto t1 = std::chrono::high_resolution_clock::now();
@@ -1398,7 +1304,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
command_count[t].num_vt++; command_count[t].num_vt++;
} else if (command.type == COMMAND_F) { } else if (command.type == COMMAND_F) {
command_count[t].num_f += command.f.size(); command_count[t].num_f += command.f.size();
command_count[t].num_indices += command.f_num_verts.size(); command_count[t].num_faces++;
} }
if (command.type == COMMAND_MTLLIB) { if (command.type == COMMAND_MTLLIB) {
@@ -1423,6 +1329,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
} }
std::map<std::string, int> material_map; std::map<std::string, int> material_map;
std::vector<material_t> materials;
// Load material(if exits) // Load material(if exits)
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 && if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
@@ -1437,7 +1344,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::ifstream ifs(material_filename); std::ifstream ifs(material_filename);
if (ifs.good()) { if (ifs.good()) {
LoadMtl(&material_map, materials, &ifs); LoadMtl(&material_map, &materials, &ifs);
// std::cout << "maetrials = " << materials.size() << std::endl; // std::cout << "maetrials = " << materials.size() << std::endl;
@@ -1461,15 +1368,14 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
size_t num_vn = 0; size_t num_vn = 0;
size_t num_vt = 0; size_t num_vt = 0;
size_t num_f = 0; size_t num_f = 0;
size_t num_indices = 0; size_t num_faces = 0;
for (size_t t = 0; t < num_threads; t++) { for (size_t t = 0; t < num_threads; t++) {
num_v += command_count[t].num_v; num_v += command_count[t].num_v;
num_vn += command_count[t].num_vn; num_vn += command_count[t].num_vn;
num_vt += command_count[t].num_vt; num_vt += command_count[t].num_vt;
num_f += command_count[t].num_f; num_f += command_count[t].num_f;
num_indices += command_count[t].num_indices; num_faces += command_count[t].num_faces;
} }
// std::cout << "# v " << num_v << std::endl; // std::cout << "# v " << num_v << std::endl;
// std::cout << "# vn " << num_vn << std::endl; // std::cout << "# vn " << num_vn << std::endl;
// std::cout << "# vt " << num_vt << std::endl; // std::cout << "# vt " << num_vt << std::endl;
@@ -1483,9 +1389,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
attrib->vertices.resize(num_v * 3); attrib->vertices.resize(num_v * 3);
attrib->normals.resize(num_vn * 3); attrib->normals.resize(num_vn * 3);
attrib->texcoords.resize(num_vt * 2); attrib->texcoords.resize(num_vt * 2);
attrib->indices.resize(num_f); attrib->faces.resize(num_f);
attrib->face_num_verts.resize(num_indices); attrib->face_num_verts.resize(num_faces);
attrib->material_ids.resize(num_indices); attrib->material_ids.resize(num_faces);
size_t v_offsets[kMaxThreads]; size_t v_offsets[kMaxThreads];
size_t n_offsets[kMaxThreads]; size_t n_offsets[kMaxThreads];
@@ -1504,7 +1410,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn; n_offsets[t] = n_offsets[t - 1] + command_count[t - 1].num_vn;
t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt; t_offsets[t] = t_offsets[t - 1] + command_count[t - 1].num_vt;
f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f; f_offsets[t] = f_offsets[t - 1] + command_count[t - 1].num_f;
face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_indices; face_offsets[t] = face_offsets[t - 1] + command_count[t - 1].num_faces;
} }
StackVector<std::thread, 16> workers; StackVector<std::thread, 16> workers;
@@ -1550,20 +1456,17 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
t_count++; t_count++;
} else if (commands[t][i].type == COMMAND_F) { } else if (commands[t][i].type == COMMAND_F) {
for (size_t k = 0; k < commands[t][i].f.size(); k++) { for (size_t k = 0; k < commands[t][i].f.size(); k++) {
index_t &vi = commands[t][i].f[k]; vertex_index &vi = commands[t][i].f[k];
int vertex_index = fixIndex(vi.vertex_index, v_count); int v_idx = fixIndex(vi.v_idx, v_count);
int texcoord_index = fixIndex(vi.texcoord_index, t_count); int vn_idx = fixIndex(vi.vn_idx, n_count);
int normal_index = fixIndex(vi.normal_index, n_count); int vt_idx = fixIndex(vi.vt_idx, t_count);
attrib->indices[f_count + k] = attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
index_t(vertex_index, texcoord_index, normal_index);
}
for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) {
attrib->material_ids[face_count + k] = material_id;
attrib->face_num_verts[face_count + k] = commands[t][i].f_num_verts[k];
} }
attrib->material_ids[face_count] = material_id;
attrib->face_num_verts[face_count] = commands[t][i].f.size();
f_count += commands[t][i].f.size(); f_count += commands[t][i].f.size();
face_count += commands[t][i].f_num_verts.size(); face_count++;
} }
} }
})); }));
@@ -1653,21 +1556,21 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
} }
std::chrono::duration<double, std::milli> ms_total = t4 - t1; std::chrono::duration<double, std::milli> ms_total = t4 - t1;
if (option.verbose) { if (option.verbose) {
std::cout << "total parsing time: " << ms_total.count() << " ms\n"; std::cout << "total parsing time: " << ms_total.count() << " ms\n";
std::cout << " line detection : " << ms_linedetection.count() << " ms\n"; std::cout << " line detection : " << ms_linedetection.count() << " ms\n";
std::cout << " alloc buf : " << ms_alloc.count() << " ms\n"; std::cout << " alloc buf : " << ms_alloc.count() << " ms\n";
std::cout << " parse : " << ms_parse.count() << " ms\n"; std::cout << " parse : " << ms_parse.count() << " ms\n";
std::cout << " merge : " << ms_merge.count() << " ms\n"; std::cout << " merge : " << ms_merge.count() << " ms\n";
std::cout << " construct : " << ms_construct.count() << " ms\n"; std::cout << " construct : " << ms_construct.count() << " ms\n";
std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n"; std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n";
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl; std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
std::cout << "# of normals = " << attrib->normals.size() << std::endl; std::cout << "# of normals = " << attrib->normals.size() << std::endl;
std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl; std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl;
std::cout << "# of face indices = " << attrib->indices.size() << std::endl; std::cout << "# of face indices = " << attrib->faces.size() << std::endl;
std::cout << "# of indices = " << attrib->material_ids.size() << std::endl; std::cout << "# of faces = " << attrib->material_ids.size() << std::endl;
std::cout << "# of shapes = " << shapes->size() << std::endl; std::cout << "# of shapes = " << shapes->size() << std::endl;
} }
return true; return true;
} }

View File

@@ -86,7 +86,7 @@ static void vsub(const float *src1, const float *src2, float *dst) {
} }
static void vcopy(const float *v1, float *v2) { static void vcopy(const float *v1, float *v2) {
int i; register int i;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
v2[i] = v1[i]; v2[i] = v1[i];
} }

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

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

View File

@@ -16,10 +16,6 @@
#include <zlib.h> #include <zlib.h>
#endif #endif
#if defined(ENABLE_ZSTD)
#include <zstd.h>
#endif
#include <GL/glew.h> #include <GL/glew.h>
#ifdef __APPLE__ #ifdef __APPLE__
@@ -186,71 +182,6 @@ bool gz_load(std::vector<char>* buf, const char* filename)
#endif #endif
} }
#ifdef ENABLE_ZSTD
static off_t fsize_X(const char *filename)
{
struct stat st;
if (stat(filename, &st) == 0) return st.st_size;
/* error */
printf("stat: %s : %s \n", filename, strerror(errno));
exit(1);
}
static FILE* fopen_X(const char *filename, const char *instruction)
{
FILE* const inFile = fopen(filename, instruction);
if (inFile) return inFile;
/* error */
printf("fopen: %s : %s \n", filename, strerror(errno));
exit(2);
}
static void* malloc_X(size_t size)
{
void* const buff = malloc(size);
if (buff) return buff;
/* error */
printf("malloc: %s \n", strerror(errno));
exit(3);
}
#endif
bool zstd_load(std::vector<char>* buf, const char* filename)
{
#ifdef ENABLE_ZSTD
off_t const buffSize = fsize_X(filename);
FILE* const inFile = fopen_X(filename, "rb");
void* const buffer = malloc_X(buffSize);
size_t const readSize = fread(buffer, 1, buffSize, inFile);
if (readSize != (size_t)buffSize) {
printf("fread: %s : %s \n", filename, strerror(errno));
exit(4);
}
fclose(inFile);
unsigned long long const rSize = ZSTD_getDecompressedSize(buffer, buffSize);
if (rSize==0) {
printf("%s : original size unknown \n", filename);
exit(5);
}
buf->resize(rSize);
size_t const dSize = ZSTD_decompress(buf->data(), rSize, buffer, buffSize);
if (dSize != rSize) {
printf("error decoding %s : %s \n", filename, ZSTD_getErrorName(dSize));
exit(7);
}
free(buffer);
return true;
#else
return false;
#endif
}
const char* get_file_data(size_t *len, const char* filename) const char* get_file_data(size_t *len, const char* filename)
{ {
@@ -273,20 +204,6 @@ const char* get_file_data(size_t *len, const char* filename)
data_len = buf.size(); data_len = buf.size();
} }
} else if (strcmp(ext, ".zst") == 0) {
printf("zstd\n");
// Zstandard data.
std::vector<char> buf;
bool ret = zstd_load(&buf, filename);
if (ret) {
char *p = static_cast<char*>(malloc(buf.size() + 1)); // @fixme { implement deleter }
memcpy(p, &buf.at(0), buf.size());
p[buf.size()] = '\0';
data = p;
data_len = buf.size();
}
} else { } else {
data = mmap_file(&data_len, filename); data = mmap_file(&data_len, filename);
@@ -297,39 +214,26 @@ const char* get_file_data(size_t *len, const char* filename)
} }
bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose) bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads)
{ {
#if 1
tinyobj_opt::attrib_t attrib; tinyobj_opt::attrib_t attrib;
std::vector<tinyobj_opt::shape_t> shapes; std::vector<tinyobj_opt::shape_t> shapes;
std::vector<tinyobj_opt::material_t> materials;
auto load_t_begin = std::chrono::high_resolution_clock::now();
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);
if (data == nullptr) { if (data == nullptr) {
printf("failed to load file\n");
exit(-1); exit(-1);
return false; return false;
} }
auto load_t_end = std::chrono::high_resolution_clock::now(); printf("filesize: %d\n", (int)data_len);
std::chrono::duration<double, std::milli> load_ms = load_t_end - load_t_begin;
if (verbose) {
std::cout << "filesize: " << data_len << std::endl;
std::cout << "load time: " << load_ms.count() << " [msecs]" << std::endl;
}
tinyobj_opt::LoadOption option; tinyobj_opt::LoadOption option;
option.req_num_threads = num_threads; option.req_num_threads = num_threads;
option.verbose = verbose; bool ret = parseObj(&attrib, &shapes, data, data_len, option);
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max(); bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max(); bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
//std::cout << "vertices.size() = " << attrib.vertices.size() << std::endl;
//std::cout << "normals.size() = " << attrib.normals.size() << std::endl;
{ {
DrawObject o; DrawObject o;
std::vector<float> vb; // pos(3float), normal(3float), color(3float) std::vector<float> vb; // pos(3float), normal(3float), color(3float)
@@ -337,15 +241,15 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) { for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face. assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face.
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) { for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0]; tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0];
tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1]; tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1];
tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2]; tinyobj_opt::vertex_index idx2 = attrib.faces[face_offset+3*f+2];
float v[3][3]; float v[3][3];
for (int k = 0; k < 3; k++) { for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index; int f0 = idx0.v_idx;
int f1 = idx1.vertex_index; int f1 = idx1.v_idx;
int f2 = idx2.vertex_index; int f2 = idx2.v_idx;
assert(f0 >= 0); assert(f0 >= 0);
assert(f1 >= 0); assert(f1 >= 0);
assert(f2 >= 0); assert(f2 >= 0);
@@ -364,24 +268,19 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
float n[3][3]; float n[3][3];
if (attrib.normals.size() > 0) { if (attrib.normals.size() > 0) {
int nf0 = idx0.normal_index; int f0 = idx0.vn_idx;
int nf1 = idx1.normal_index; int f1 = idx1.vn_idx;
int nf2 = idx2.normal_index; int f2 = idx2.vn_idx;
assert(f0 >= 0);
if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) { assert(f1 >= 0);
assert(3*nf0+2 < attrib.normals.size()); assert(f2 >= 0);
assert(3*nf1+2 < attrib.normals.size()); assert(3*f0+2 < attrib.normals.size());
assert(3*nf2+2 < attrib.normals.size()); assert(3*f1+2 < attrib.normals.size());
for (int k = 0; k < 3; k++) { assert(3*f2+2 < attrib.normals.size());
n[0][k] = attrib.normals[3*nf0+k]; for (int k = 0; k < 3; k++) {
n[1][k] = attrib.normals[3*nf1+k]; n[0][k] = attrib.normals[3*f0+k];
n[2][k] = attrib.normals[3*nf2+k]; n[1][k] = attrib.normals[3*f1+k];
} n[2][k] = attrib.normals[3*f2+k];
} else {
// compute geometric normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2];
n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2];
} }
} else { } else {
// compute geometric normal // compute geometric normal
@@ -400,7 +299,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
// Use normal as color. // Use normal as color.
float c[3] = {n[k][0], n[k][1], n[k][2]}; float c[3] = {n[k][0], n[k][1], n[k][2]};
float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2];
if (len2 > 1.0e-6f) { if (len2 > 0.0f) {
float len = sqrtf(len2); float len = sqrtf(len2);
c[0] /= len; c[0] /= len;
@@ -431,6 +330,9 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
return true; return true;
#else
return false;
#endif
} }
void reshapeFunc(GLFWwindow* window, int w, int h) void reshapeFunc(GLFWwindow* window, int w, int h)
@@ -597,45 +499,35 @@ static void Init() {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc < 2) { if (argc < 2) {
std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << std::endl; std::cout << "Needs input.obj\n" << std::endl;
return 0; return 0;
} }
bool benchmark_only = false; bool benchmark_only = false;
int num_threads = -1; int num_threads = -1;
bool verbose = false;
if (argc > 2) { if (argc > 2) {
num_threads = atoi(argv[2]); num_threads = atoi(argv[2]);
} }
if (argc > 3) { if (argc > 3) {
benchmark_only = (atoi(argv[3]) > 0) ? true : false; benchmark_only = true;
}
if (argc > 4) {
verbose = true;
} }
if (benchmark_only) { if (benchmark_only) {
tinyobj_opt::attrib_t attrib; tinyobj_opt::attrib_t attrib;
std::vector<tinyobj_opt::shape_t> shapes; std::vector<tinyobj_opt::shape_t> shapes;
std::vector<tinyobj_opt::material_t> materials;
size_t data_len = 0; size_t data_len = 0;
const char* data = get_file_data(&data_len, argv[1]); const char* data = get_file_data(&data_len, argv[1]);
if (data == nullptr) { if (data == nullptr) {
printf("failed to load file\n");
exit(-1); exit(-1);
return false; return false;
} }
printf("filesize: %d\n", (int)data_len); printf("filesize: %d\n", (int)data_len);
tinyobj_opt::LoadOption option; tinyobj_opt::LoadOption option;
option.req_num_threads = num_threads; option.req_num_threads = num_threads;
option.verbose = true; bool ret = parseObj(&attrib, &shapes, data, data_len, option);
bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
return ret; return ret;
} }
@@ -677,7 +569,7 @@ int main(int argc, char **argv)
reshapeFunc(window, width, height); reshapeFunc(window, width, height);
float bmin[3], bmax[3]; float bmin[3], bmax[3];
if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) { if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads)) {
printf("failed to load & conv\n"); printf("failed to load & conv\n");
return -1; return -1;
} }

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
mtllib issue-92.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

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

View File

@@ -1,7 +0,0 @@
mtllib issue-95-2.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

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

View File

@@ -1,7 +0,0 @@
mtllib issue-95.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

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

View File

@@ -1,7 +0,0 @@
mtllib invalid-file-aaa.mtl invalid-file-bbb.mtl mtllib-multiple-files-issue-112.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

@@ -1,19 +0,0 @@
# .MTL with PBR extension.
newmtl pbr
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
Ke 0.1 0.1 0.1
Pr 0.2
Pm 0.3
Ps 0.4
Pc 0.5
Pcr 0.6
aniso 0.7
anisor 0.8
map_Pr roughness.tex
map_Pm metallic.tex
map_Ps sheen.tex
map_Ke emissive.tex
norm normalmap.tex

View File

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

View File

@@ -1,36 +0,0 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
map_Ka -clamp on ambient.jpg
map_Kd -o 0.1 diffuse.jpg
map_Ks -s 0.1 0.2 specular.jpg
map_Ns -t 0.1 0.2 0.3 specular_highlight.jpg
map_bump -bm 3 bumpmap.jpg
newmtl bm2
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
# blendu
map_Kd -blendu on diffuse.jpg
map_Ks -blendv off specular.jpg
map_Ns -mm 0.1 0.3 specular_highlight.jpg
# -bm after filename
map_bump -imfchan r bumpmap2.jpg -bm 1.5
newmtl bm3
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
# type
map_Kd -type sphere diffuse.jpg
map_Ks -type cube_top specular.jpg
map_Ns -type cube_bottom specular_highlight.jpg
map_Ka -type cube_left ambient.jpg
map_d -type cube_right alpha.jpg
map_bump -type cube_front bump.jpg
disp -type cube_back displacement.jpg

View File

@@ -1,7 +0,0 @@
mtllib texture-options-issue-85.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

@@ -1,30 +0,0 @@
# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project.
# original cornell box data
# comment
# empty line including some space
mtllib cornell_box.mtl
o floor
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
v 130.0 0.0 65.0
v 82.0 0.0 225.0
v 240.0 0.0 272.0
v 290.0 0.0 114.0
v 423.0 0.0 247.0
v 265.0 0.0 296.0
v 314.0 0.0 456.0
v 472.0 0.0 406.0
f 1 2 3 4
f 8 7 6 5
f 12 11 10 9
usemtl white

View File

@@ -1,2 +0,0 @@
* PBR material
* Define index_t struct

View File

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

View File

@@ -184,48 +184,6 @@ TestLoadObj(
return true; return true;
} }
static bool
TestLoadObjFromPreopenedFile(
const char* filename,
const char* basepath = NULL,
bool readMaterials = true,
bool triangulate = true)
{
std::string fullFilename = std::string(basepath) + filename;
std::cout << "Loading " << fullFilename << std::endl;
std::ifstream fileStream(fullFilename.c_str());
if (!fileStream) {
std::cerr << "Could not find specified file: " << fullFilename << std::endl;
return false;
}
tinyobj::MaterialStreamReader materialStreamReader(fileStream);
tinyobj::MaterialStreamReader* materialReader = readMaterials
? &materialStreamReader
: NULL;
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, &fileStream, materialReader);
if (!err.empty()) {
std::cerr << err << std::endl;
}
if (!ret) {
printf("Failed to load/parse .obj.\n");
return false;
}
std::cout << "Loaded material count: " << materials.size() << "\n";
return true;
}
static bool static bool
TestStreamLoadObj() TestStreamLoadObj()
@@ -335,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]") {
@@ -361,190 +319,10 @@ TEST_CASE("catmark_torus_creases0", "[Loader]") {
REQUIRE(8 == shapes[0].mesh.tags.size()); REQUIRE(8 == shapes[0].mesh.tags.size());
} }
TEST_CASE("pbr", "[Loader]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/pbr-mat-ext.obj", gMtlBasePath, /*triangulate*/false);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
REQUIRE(0.2 == Approx(materials[0].roughness));
REQUIRE(0.3 == Approx(materials[0].metallic));
REQUIRE(0.4 == Approx(materials[0].sheen));
REQUIRE(0.5 == Approx(materials[0].clearcoat_thickness));
REQUIRE(0.6 == Approx(materials[0].clearcoat_roughness));
REQUIRE(0.7 == Approx(materials[0].anisotropy));
REQUIRE(0.8 == Approx(materials[0].anisotropy_rotation));
REQUIRE(0 == materials[0].roughness_texname.compare("roughness.tex"));
REQUIRE(0 == materials[0].metallic_texname.compare("metallic.tex"));
REQUIRE(0 == materials[0].sheen_texname.compare("sheen.tex"));
REQUIRE(0 == materials[0].emissive_texname.compare("emissive.tex"));
REQUIRE(0 == materials[0].normal_texname.compare("normalmap.tex"));
}
TEST_CASE("stream_load", "[Stream]") { TEST_CASE("stream_load", "[Stream]") {
REQUIRE(true == TestStreamLoadObj()); REQUIRE(true == TestStreamLoadObj());
} }
TEST_CASE("stream_load_from_file_skipping_materials", "[Stream]") {
REQUIRE(true == TestLoadObjFromPreopenedFile(
"../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/false, /*triangulate*/false));
}
TEST_CASE("stream_load_from_file_with_materials", "[Stream]") {
REQUIRE(true == TestLoadObjFromPreopenedFile(
"../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/true, /*triangulate*/false));
}
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]));
}
TEST_CASE("usemtl_at_last_line", "[Issue104]") {
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/usemtl-issue-104.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == shapes.size());
}
TEST_CASE("texture_opts", "[Issue85]") {
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/texture-options-issue-85.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == shapes.size());
REQUIRE(3 == materials.size());
REQUIRE(0 == materials[0].name.compare("default"));
REQUIRE(0 == materials[1].name.compare("bm2"));
REQUIRE(0 == materials[2].name.compare("bm3"));
REQUIRE(true == materials[0].ambient_texopt.clamp);
REQUIRE(0.1 == Approx(materials[0].diffuse_texopt.origin_offset[0]));
REQUIRE(0.0 == Approx(materials[0].diffuse_texopt.origin_offset[1]));
REQUIRE(0.0 == Approx(materials[0].diffuse_texopt.origin_offset[2]));
REQUIRE(0.1 == Approx(materials[0].specular_texopt.scale[0]));
REQUIRE(0.2 == Approx(materials[0].specular_texopt.scale[1]));
REQUIRE(1.0 == Approx(materials[0].specular_texopt.scale[2]));
REQUIRE(0.1 == Approx(materials[0].specular_highlight_texopt.turbulence[0]));
REQUIRE(0.2 == Approx(materials[0].specular_highlight_texopt.turbulence[1]));
REQUIRE(0.3 == Approx(materials[0].specular_highlight_texopt.turbulence[2]));
REQUIRE(3.0 == Approx(materials[0].bump_texopt.bump_multiplier));
REQUIRE(0.1 == Approx(materials[1].specular_highlight_texopt.brightness));
REQUIRE(0.3 == Approx(materials[1].specular_highlight_texopt.contrast));
REQUIRE('r' == materials[1].bump_texopt.imfchan);
REQUIRE(tinyobj::TEXTURE_TYPE_SPHERE == materials[2].diffuse_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_TOP == materials[2].specular_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM == materials[2].specular_highlight_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_LEFT == materials[2].ambient_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_RIGHT == materials[2].alpha_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_FRONT == materials[2].bump_texopt.type);
REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BACK == materials[2].displacement_texopt.type);
}
TEST_CASE("mtllib_multiple_filenames", "[Issue112]") {
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/mtllib-multiple-files-issue-112.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == materials.size());
}
#if 0 #if 0
int int
main( main(

File diff suppressed because it is too large Load Diff