Refactor source code of optimized .obj parser.
This commit is contained in:
@@ -1,35 +1,64 @@
|
||||
//
|
||||
//@todo { parse material. assign material id }
|
||||
//@todo { support object&group }
|
||||
// Optimized wavefront .obj loader.
|
||||
// Requires ltalloc and C++11
|
||||
//
|
||||
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2016 Syoyo Fujita and many contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
#ifndef TINOBJ_LOADER_OPT_H_
|
||||
#define TINOBJ_LOADER_OPT_H_
|
||||
|
||||
#ifdef _WIN64
|
||||
#define atoll(S) _atoi64(S)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <atomic> // C++11
|
||||
#include <chrono> // C++11
|
||||
#include <thread> // C++11
|
||||
#include <atomic> // C++11
|
||||
|
||||
#include "ltalloc.hpp"
|
||||
|
||||
namespace tinyobj_opt {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Small vector class useful for multi-threaded environment.
|
||||
//
|
||||
@@ -272,6 +301,23 @@ typedef struct {
|
||||
unsigned int length;
|
||||
} shape_t;
|
||||
|
||||
struct vertex_index {
|
||||
int v_idx, vt_idx, vn_idx;
|
||||
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
|
||||
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
|
||||
vertex_index(int vidx, int vtidx, int vnidx)
|
||||
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
std::vector<float, lt::allocator<float> > vertices;
|
||||
std::vector<float, lt::allocator<float> > normals;
|
||||
std::vector<float, lt::allocator<float> > texcoords;
|
||||
std::vector<vertex_index, lt::allocator<vertex_index> > faces;
|
||||
std::vector<int, lt::allocator<int> > face_num_verts;
|
||||
std::vector<int, lt::allocator<int> > material_ids;
|
||||
} attrib_t;
|
||||
|
||||
typedef StackVector<char, 256> ShortString;
|
||||
|
||||
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
|
||||
@@ -279,22 +325,19 @@ typedef StackVector<char, 256> ShortString;
|
||||
(static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
|
||||
#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
|
||||
|
||||
static inline void skip_space(const char **token)
|
||||
{
|
||||
static inline void skip_space(const char **token) {
|
||||
while ((*token)[0] == ' ' || (*token)[0] == '\t') {
|
||||
(*token)++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void skip_space_and_cr(const char **token)
|
||||
{
|
||||
static inline void skip_space_and_cr(const char **token) {
|
||||
while ((*token)[0] == ' ' || (*token)[0] == '\t' || (*token)[0] == '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int until_space(const char *token)
|
||||
{
|
||||
static inline int until_space(const char *token) {
|
||||
const char *p = token;
|
||||
while (p[0] != '\0' && p[0] != ' ' && p[0] != '\t' && p[0] != '\r') {
|
||||
p++;
|
||||
@@ -303,8 +346,7 @@ static inline int until_space(const char *token)
|
||||
return p - token;
|
||||
}
|
||||
|
||||
static inline int length_until_newline(const char *token, int n)
|
||||
{
|
||||
static inline int length_until_newline(const char *token, int n) {
|
||||
int len = 0;
|
||||
|
||||
// Assume token[n-1] = '\0'
|
||||
@@ -336,14 +378,6 @@ static inline int my_atoi( const char *c ) {
|
||||
return value * sign;
|
||||
}
|
||||
|
||||
struct vertex_index {
|
||||
int v_idx, vt_idx, vn_idx;
|
||||
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
|
||||
explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
|
||||
vertex_index(int vidx, int vtidx, int vnidx)
|
||||
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
|
||||
};
|
||||
|
||||
// Make index zero-base, and also support relative index.
|
||||
static inline int fixIndex(int idx, int n) {
|
||||
if (idx > 0) return idx - 1;
|
||||
@@ -351,55 +385,6 @@ static inline int fixIndex(int idx, int n) {
|
||||
return n + idx; // negative value = relative
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Parse triples with index offsets: i, i/j/k, i//k, i/j
|
||||
static vertex_index parseTriple(const char **token, int vsize, int vnsize,
|
||||
int vtsize) {
|
||||
vertex_index vi(-1);
|
||||
|
||||
vi.v_idx = fixIndex(my_atoi((*token)), vsize);
|
||||
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
if ((*token)[0] != '/') {
|
||||
return vi;
|
||||
}
|
||||
(*token)++;
|
||||
|
||||
// i//k
|
||||
if ((*token)[0] == '/') {
|
||||
(*token)++;
|
||||
vi.vn_idx = fixIndex(my_atoi((*token)), vnsize);
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
return vi;
|
||||
}
|
||||
|
||||
// i/j/k or i/j
|
||||
vi.vt_idx = fixIndex(my_atoi((*token)), vtsize);
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
if ((*token)[0] != '/') {
|
||||
return vi;
|
||||
}
|
||||
|
||||
// i/j/k
|
||||
(*token)++; // skip '/'
|
||||
vi.vn_idx = fixIndex(my_atoi((*token)), vnsize);
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
return vi;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parse raw triples: i, i/j/k, i//k, i/j
|
||||
static vertex_index parseRawTriple(const char **token) {
|
||||
vertex_index vi(
|
||||
@@ -407,7 +392,8 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
|
||||
vi.v_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
if ((*token)[0] != '/') {
|
||||
@@ -420,7 +406,8 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
(*token)++;
|
||||
vi.vn_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
return vi;
|
||||
@@ -429,7 +416,8 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
// i/j/k or i/j
|
||||
vi.vt_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
if ((*token)[0] != '/') {
|
||||
@@ -440,7 +428,8 @@ static vertex_index parseRawTriple(const char **token) {
|
||||
(*token)++; // skip '/'
|
||||
vi.vn_idx = my_atoi((*token));
|
||||
//(*token) += strcspn((*token), "/ \t\r");
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' && (*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
while ((*token)[0] != '\0' && (*token)[0] != '/' && (*token)[0] != ' ' &&
|
||||
(*token)[0] != '\t' && (*token)[0] != '\r') {
|
||||
(*token)++;
|
||||
}
|
||||
return vi;
|
||||
@@ -461,7 +450,6 @@ static inline int parseInt(const char **token) {
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Tries to parse a floating point number located at s.
|
||||
//
|
||||
// s_end should be a location in the string where reading should absolutely
|
||||
@@ -549,7 +537,6 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
|
||||
read = 1;
|
||||
end_not_reached = (curr != s_end);
|
||||
while (end_not_reached && IS_DIGIT(*curr)) {
|
||||
|
||||
// pow(10.0, -read)
|
||||
double frac_value = 1.0;
|
||||
for (int f = 0; f < read; f++) {
|
||||
@@ -604,7 +591,8 @@ assemble:
|
||||
}
|
||||
*result =
|
||||
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
|
||||
(sign == '+' ? 1 : -1) * (mantissa * a) * static_cast<double>(1ULL << exponent); // 5.0^exponent * 2^exponent
|
||||
(sign == '+' ? 1 : -1) * (mantissa * a) *
|
||||
static_cast<double>(1ULL << exponent); // 5.0^exponent * 2^exponent
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -618,7 +606,8 @@ static inline float parseFloat(const char **token) {
|
||||
float f = static_cast<float>(atof(*token));
|
||||
(*token) += strcspn((*token), " \t\r");
|
||||
#else
|
||||
const char *end = (*token) + until_space((*token)); //strcspn((*token), " \t\r");
|
||||
const char *end =
|
||||
(*token) + until_space((*token)); // strcspn((*token), " \t\r");
|
||||
double val = 0.0;
|
||||
tryParseDouble((*token), end, &val);
|
||||
float f = static_cast<float>(val);
|
||||
@@ -663,7 +652,8 @@ static void InitMaterial(material_t *material) {
|
||||
}
|
||||
|
||||
static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
std::vector<material_t> *materials, std::istream *inStream) {
|
||||
std::vector<material_t> *materials,
|
||||
std::istream *inStream) {
|
||||
// Create a default material anyway.
|
||||
material_t material;
|
||||
InitMaterial(&material);
|
||||
@@ -887,8 +877,7 @@ static void LoadMtl(std::map<std::string, int> *material_map,
|
||||
materials->push_back(material);
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
COMMAND_EMPTY,
|
||||
COMMAND_V,
|
||||
COMMAND_VN,
|
||||
@@ -901,8 +890,7 @@ typedef enum
|
||||
|
||||
} CommandType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
float vx, vy, vz;
|
||||
float nx, ny, nz;
|
||||
float tx, ty;
|
||||
@@ -925,8 +913,7 @@ typedef struct
|
||||
CommandType type;
|
||||
} Command;
|
||||
|
||||
struct CommandCount
|
||||
{
|
||||
struct CommandCount {
|
||||
size_t num_v;
|
||||
size_t num_vn;
|
||||
size_t num_vt;
|
||||
@@ -941,8 +928,16 @@ struct CommandCount
|
||||
}
|
||||
};
|
||||
|
||||
static bool parseLine(Command *command, const char *p, size_t p_len, bool triangulate = true)
|
||||
{
|
||||
/// Parse wavefront .obj(.obj string data is expanded to linear char array
|
||||
/// `buf')
|
||||
/// -1 to req_num_threads use the number of HW threads in the running system.
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
size_t len, int req_num_threads = -1, bool triangulate = true);
|
||||
|
||||
#ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
||||
|
||||
static bool parseLine(Command *command, const char *p, size_t p_len,
|
||||
bool triangulate = true) {
|
||||
char linebuf[4096];
|
||||
assert(p_len < 4095);
|
||||
// StackVector<char, 256> linebuf;
|
||||
@@ -1041,7 +1036,6 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (size_t k = 0; k < f->size(); k++) {
|
||||
command->f.emplace_back(f[k]);
|
||||
}
|
||||
@@ -1067,11 +1061,13 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang
|
||||
// materialId = newMaterialId;
|
||||
//}
|
||||
|
||||
//command->material_name = .insert(command->material_name->end(), namebuf, namebuf + strlen(namebuf));
|
||||
// command->material_name = .insert(command->material_name->end(), namebuf,
|
||||
// namebuf + strlen(namebuf));
|
||||
// command->material_name->push_back('\0');
|
||||
skip_space(&token);
|
||||
command->material_name = p + (token - linebuf);
|
||||
command->material_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->material_name_len =
|
||||
length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->type = COMMAND_USEMTL;
|
||||
|
||||
return true;
|
||||
@@ -1084,7 +1080,8 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang
|
||||
|
||||
skip_space(&token);
|
||||
command->mtllib_name = p + (token - linebuf);
|
||||
command->mtllib_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->mtllib_name_len =
|
||||
length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->type = COMMAND_MTLLIB;
|
||||
|
||||
return true;
|
||||
@@ -1096,7 +1093,8 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang
|
||||
token += 2;
|
||||
|
||||
command->group_name = p + (token - linebuf);
|
||||
command->group_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->group_name_len =
|
||||
length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->type = COMMAND_G;
|
||||
|
||||
return true;
|
||||
@@ -1108,18 +1106,17 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang
|
||||
token += 2;
|
||||
|
||||
command->object_name = p + (token - linebuf);
|
||||
command->object_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->object_name_len =
|
||||
length_until_newline(token, p_len - (token - linebuf)) + 1;
|
||||
command->type = COMMAND_O;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
size_t pos;
|
||||
size_t len;
|
||||
} LineInfo;
|
||||
@@ -1132,8 +1129,7 @@ typedef struct
|
||||
|
||||
#define kMaxThreads (32)
|
||||
|
||||
static inline bool is_line_ending(const char* p, size_t i, size_t end_i)
|
||||
{
|
||||
static inline bool is_line_ending(const char *p, size_t i, size_t end_i) {
|
||||
if (p[i] == '\0') return true;
|
||||
if (p[i] == '\n') return true; // this includes \r\n
|
||||
if (p[i] == '\r') {
|
||||
@@ -1144,29 +1140,30 @@ static inline bool is_line_ending(const char* p, size_t i, size_t end_i)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float, lt::allocator<float>> &normals, std::vector<float, lt::allocator<float>> &texcoords, std::vector<vertex_index, lt::allocator<vertex_index>> &faces, std::vector<int, lt::allocator<int> > &material_ids, std::vector<shape_t>& shapes, const char* buf, size_t len, int req_num_threads)
|
||||
{
|
||||
|
||||
vertices.clear();
|
||||
normals.clear();
|
||||
texcoords.clear();
|
||||
faces.clear();
|
||||
material_ids.clear();
|
||||
shapes.clear();
|
||||
bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
|
||||
size_t len, int req_num_threads, bool triangulate) {
|
||||
attrib->vertices.clear();
|
||||
attrib->normals.clear();
|
||||
attrib->texcoords.clear();
|
||||
attrib->faces.clear();
|
||||
attrib->face_num_verts.clear();
|
||||
attrib->material_ids.clear();
|
||||
shapes->clear();
|
||||
|
||||
if (len < 1) return false;
|
||||
|
||||
std::cout << "parse" << std::endl;
|
||||
|
||||
auto num_threads = (req_num_threads < 0) ? std::thread::hardware_concurrency() : req_num_threads;
|
||||
num_threads = std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
|
||||
auto num_threads = (req_num_threads < 0) ? std::thread::hardware_concurrency()
|
||||
: req_num_threads;
|
||||
num_threads =
|
||||
std::max(1, std::min(static_cast<int>(num_threads), kMaxThreads));
|
||||
std::cout << "# of threads = " << num_threads << std::endl;
|
||||
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::vector<LineInfo, lt::allocator<LineInfo> > line_infos[kMaxThreads];
|
||||
for (size_t t = 0; t < static_cast<size_t>(num_threads); t++) {
|
||||
// Pre allocate enough memory. len / 128 / num_threads is just a heuristic value.
|
||||
// Pre allocate enough memory. len / 128 / num_threads is just a heuristic
|
||||
// value.
|
||||
line_infos[t].reserve(len / 128 / num_threads);
|
||||
}
|
||||
|
||||
@@ -1177,7 +1174,6 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
std::chrono::duration<double, std::milli> ms_merge;
|
||||
std::chrono::duration<double, std::milli> ms_construct;
|
||||
|
||||
|
||||
// 1. Find '\n' and create line data.
|
||||
{
|
||||
StackVector<std::thread, 16> workers;
|
||||
@@ -1196,8 +1192,10 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
size_t prev_pos = start_idx;
|
||||
for (size_t i = start_idx; i < end_idx; i++) {
|
||||
if (is_line_ending(buf, i, end_idx)) {
|
||||
if ((t > 0) && (prev_pos == start_idx) && (!is_line_ending(buf, start_idx-1, end_idx))) {
|
||||
// first linebreak found in (chunk > 0), and a line before this linebreak belongs to previous chunk, so skip it.
|
||||
if ((t > 0) && (prev_pos == start_idx) &&
|
||||
(!is_line_ending(buf, start_idx - 1, end_idx))) {
|
||||
// first linebreak found in (chunk > 0), and a line before this
|
||||
// linebreak belongs to previous chunk, so skip it.
|
||||
prev_pos = i + 1;
|
||||
continue;
|
||||
} else {
|
||||
@@ -1251,20 +1249,18 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
// std::cout << "# of lines = " << line_sum << std::endl;
|
||||
|
||||
std::vector<Command> commands[kMaxThreads];
|
||||
//thread_local std::vector<Command, lt::allocator<Command> > commands;
|
||||
|
||||
// 2. allocate buffer
|
||||
auto t_alloc_start = std::chrono::high_resolution_clock::now();
|
||||
{
|
||||
|
||||
for (size_t t = 0; t < num_threads; t++) {
|
||||
commands[t].reserve(line_infos[t].size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CommandCount command_count[kMaxThreads];
|
||||
// Array index to `mtllib` line. According to wavefront .obj spec, `mtllib' should appear only once in .obj.
|
||||
// Array index to `mtllib` line. According to wavefront .obj spec, `mtllib'
|
||||
// should appear only once in .obj.
|
||||
int mtllib_t_index = -1;
|
||||
int mtllib_i_index = -1;
|
||||
|
||||
@@ -1280,7 +1276,8 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
|
||||
for (size_t i = 0; i < line_infos[t].size(); i++) {
|
||||
Command command;
|
||||
bool ret = parseLine(&command, &buf[line_infos[t][i].pos], line_infos[t][i].len);
|
||||
bool ret = parseLine(&command, &buf[line_infos[t][i].pos],
|
||||
line_infos[t][i].len, triangulate);
|
||||
if (ret) {
|
||||
if (command.type == COMMAND_V) {
|
||||
command_count[t].num_v++;
|
||||
@@ -1299,7 +1296,6 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
}
|
||||
|
||||
commands[t].emplace_back(std::move(command));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1313,16 +1309,19 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
ms_parse = t_end - t_start;
|
||||
|
||||
}
|
||||
|
||||
std::map<std::string, int> material_map;
|
||||
std::vector<material_t> materials;
|
||||
|
||||
// Load material(if exits)
|
||||
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 && commands[mtllib_t_index][mtllib_i_index].mtllib_name && commands[mtllib_t_index][mtllib_i_index].mtllib_name_len > 0) {
|
||||
std::string material_filename = std::string(commands[mtllib_t_index][mtllib_i_index].mtllib_name, commands[mtllib_t_index][mtllib_i_index].mtllib_name_len);
|
||||
std::cout << "mtllib :" << material_filename << std::endl;
|
||||
if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
|
||||
commands[mtllib_t_index][mtllib_i_index].mtllib_name &&
|
||||
commands[mtllib_t_index][mtllib_i_index].mtllib_name_len > 0) {
|
||||
std::string material_filename =
|
||||
std::string(commands[mtllib_t_index][mtllib_i_index].mtllib_name,
|
||||
commands[mtllib_t_index][mtllib_i_index].mtllib_name_len);
|
||||
// std::cout << "mtllib :" << material_filename << std::endl;
|
||||
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -1330,7 +1329,7 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
if (ifs.good()) {
|
||||
LoadMtl(&material_map, &materials, &ifs);
|
||||
|
||||
std::cout << "maetrials = " << materials.size() << std::endl;
|
||||
// std::cout << "maetrials = " << materials.size() << std::endl;
|
||||
|
||||
ifs.close();
|
||||
}
|
||||
@@ -1342,7 +1341,8 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
|
||||
auto command_sum = 0;
|
||||
for (size_t t = 0; t < num_threads; t++) {
|
||||
//std::cout << t << ": # of commands = " << commands[t].size() << std::endl;
|
||||
// std::cout << t << ": # of commands = " << commands[t].size() <<
|
||||
// std::endl;
|
||||
command_sum += commands[t].size();
|
||||
}
|
||||
// std::cout << "# of commands = " << command_sum << std::endl;
|
||||
@@ -1369,11 +1369,12 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
{
|
||||
auto t_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
vertices.resize(num_v * 3);
|
||||
normals.resize(num_vn * 3);
|
||||
texcoords.resize(num_vt * 2);
|
||||
faces.resize(num_f);
|
||||
material_ids.resize(num_faces);
|
||||
attrib->vertices.resize(num_v * 3);
|
||||
attrib->normals.resize(num_vn * 3);
|
||||
attrib->texcoords.resize(num_vt * 2);
|
||||
attrib->faces.resize(num_f);
|
||||
attrib->face_num_verts.resize(num_faces);
|
||||
attrib->material_ids.resize(num_faces);
|
||||
|
||||
size_t v_offsets[kMaxThreads];
|
||||
size_t n_offsets[kMaxThreads];
|
||||
@@ -1410,8 +1411,10 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
if (commands[t][i].type == COMMAND_EMPTY) {
|
||||
continue;
|
||||
} else if (commands[t][i].type == COMMAND_USEMTL) {
|
||||
if (commands[t][i].material_name && commands[t][i].material_name_len > 0) {
|
||||
std::string material_name(commands[t][i].material_name, commands[t][i].material_name_len);
|
||||
if (commands[t][i].material_name &&
|
||||
commands[t][i].material_name_len > 0) {
|
||||
std::string material_name(commands[t][i].material_name,
|
||||
commands[t][i].material_name_len);
|
||||
|
||||
if (material_map.find(material_name) != material_map.end()) {
|
||||
material_id = material_map[material_name];
|
||||
@@ -1421,18 +1424,18 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
}
|
||||
}
|
||||
} else if (commands[t][i].type == COMMAND_V) {
|
||||
vertices[3*v_count+0] = commands[t][i].vx;
|
||||
vertices[3*v_count+1] = commands[t][i].vy;
|
||||
vertices[3*v_count+2] = commands[t][i].vz;
|
||||
attrib->vertices[3 * v_count + 0] = commands[t][i].vx;
|
||||
attrib->vertices[3 * v_count + 1] = commands[t][i].vy;
|
||||
attrib->vertices[3 * v_count + 2] = commands[t][i].vz;
|
||||
v_count++;
|
||||
} else if (commands[t][i].type == COMMAND_VN) {
|
||||
normals[3*n_count+0] = commands[t][i].nx;
|
||||
normals[3*n_count+1] = commands[t][i].ny;
|
||||
normals[3*n_count+2] = commands[t][i].nz;
|
||||
attrib->normals[3 * n_count + 0] = commands[t][i].nx;
|
||||
attrib->normals[3 * n_count + 1] = commands[t][i].ny;
|
||||
attrib->normals[3 * n_count + 2] = commands[t][i].nz;
|
||||
n_count++;
|
||||
} else if (commands[t][i].type == COMMAND_VT) {
|
||||
texcoords[2*t_count+0] = commands[t][i].tx;
|
||||
texcoords[2*t_count+1] = commands[t][i].ty;
|
||||
attrib->texcoords[2 * t_count + 0] = commands[t][i].tx;
|
||||
attrib->texcoords[2 * t_count + 1] = commands[t][i].ty;
|
||||
t_count++;
|
||||
} else if (commands[t][i].type == COMMAND_F) {
|
||||
for (size_t k = 0; k < commands[t][i].f.size(); k++) {
|
||||
@@ -1440,9 +1443,10 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
int v_idx = fixIndex(vi.v_idx, v_count);
|
||||
int vn_idx = fixIndex(vi.vn_idx, n_count);
|
||||
int vt_idx = fixIndex(vi.vt_idx, t_count);
|
||||
faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
|
||||
attrib->faces[f_count + k] = vertex_index(v_idx, vn_idx, vt_idx);
|
||||
}
|
||||
material_ids[face_count] = material_id;
|
||||
attrib->material_ids[face_count] = material_id;
|
||||
attrib->face_num_verts[face_count] = commands[t][i].f.size();
|
||||
|
||||
f_count += commands[t][i].f.size();
|
||||
face_count++;
|
||||
@@ -1473,12 +1477,15 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
int face_prev_offset = 0;
|
||||
for (size_t t = 0; t < num_threads; t++) {
|
||||
for (size_t i = 0; i < commands[t].size(); i++) {
|
||||
if (commands[t][i].type == COMMAND_O || commands[t][i].type == COMMAND_G) {
|
||||
if (commands[t][i].type == COMMAND_O ||
|
||||
commands[t][i].type == COMMAND_G) {
|
||||
std::string name;
|
||||
if (commands[t][i].type == COMMAND_O) {
|
||||
name = std::string(commands[t][i].object_name, commands[t][i].object_name_len);
|
||||
name = std::string(commands[t][i].object_name,
|
||||
commands[t][i].object_name_len);
|
||||
} else {
|
||||
name = std::string(commands[t][i].group_name, commands[t][i].group_name_len);
|
||||
name = std::string(commands[t][i].group_name,
|
||||
commands[t][i].group_name_len);
|
||||
}
|
||||
|
||||
if (face_count == 0) {
|
||||
@@ -1487,19 +1494,19 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
shape.face_offset = face_count;
|
||||
face_prev_offset = face_count;
|
||||
} else {
|
||||
if (shapes.size() == 0) {
|
||||
if (shapes->size() == 0) {
|
||||
// 'o' or 'g' after some 'v' lines.
|
||||
// create a shape with null name
|
||||
shape.length = face_count - face_prev_offset;
|
||||
face_prev_offset = face_count;
|
||||
|
||||
shapes.push_back(shape);
|
||||
shapes->push_back(shape);
|
||||
|
||||
} else {
|
||||
if ((face_count - face_prev_offset) > 0) {
|
||||
// push previous shape
|
||||
shape.length = face_count - face_prev_offset;
|
||||
shapes.push_back(shape);
|
||||
shapes->push_back(shape);
|
||||
face_prev_offset = face_count;
|
||||
}
|
||||
}
|
||||
@@ -1509,8 +1516,6 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
shape.face_offset = face_count;
|
||||
shape.length = 0;
|
||||
}
|
||||
} else if (commands[t][i].type == COMMAND_G) {
|
||||
std::cout << "g" << std::endl;
|
||||
}
|
||||
if (commands[t][i].type == COMMAND_F) {
|
||||
face_count++;
|
||||
@@ -1521,11 +1526,11 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
if ((face_count - face_prev_offset) > 0) {
|
||||
shape.length = face_count - shape.face_offset;
|
||||
if (shape.length > 0) {
|
||||
shapes.push_back(shape);
|
||||
shapes->push_back(shape);
|
||||
}
|
||||
} else {
|
||||
// Guess no 'v' line occurrence after 'o' or 'g', so discards current shape information.
|
||||
std::cout << "remainder " << shape.name << ", " << shape.face_offset << ", " << face_prev_offset << ", " << face_count << std::endl;
|
||||
// Guess no 'v' line occurrence after 'o' or 'g', so discards current
|
||||
// shape information.
|
||||
}
|
||||
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
@@ -1541,86 +1546,17 @@ bool parse(std::vector<float, lt::allocator<float>> &vertices, std::vector<float
|
||||
std::cout << " merge : " << ms_merge.count() << " ms\n";
|
||||
std::cout << " construct : " << ms_construct.count() << " ms\n";
|
||||
std::cout << " mtl load : " << ms_load_mtl.count() << " ms\n";
|
||||
std::cout << "# of vertices = " << vertices.size() << std::endl;
|
||||
std::cout << "# of normals = " << normals.size() << std::endl;
|
||||
std::cout << "# of texcoords = " << texcoords.size() << std::endl;
|
||||
std::cout << "# of face indices = " << faces.size() << std::endl;
|
||||
std::cout << "# of faces = " << material_ids.size() << std::endl;
|
||||
std::cout << "# of shapes = " << shapes.size() << std::endl;
|
||||
|
||||
std::cout << "# of vertices = " << attrib->vertices.size() << std::endl;
|
||||
std::cout << "# of normals = " << attrib->normals.size() << std::endl;
|
||||
std::cout << "# of texcoords = " << attrib->texcoords.size() << std::endl;
|
||||
std::cout << "# of face indices = " << attrib->faces.size() << std::endl;
|
||||
std::cout << "# of faces = " << attrib->material_ids.size() << std::endl;
|
||||
std::cout << "# of shapes = " << shapes->size() << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
||||
|
||||
#ifdef CONSOLE_TEST
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
} // namespace tinyobj_opt
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Needs .obj\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int req_num_threads = -1;
|
||||
if (argc > 2) {
|
||||
req_num_threads = atoi(argv[2]);
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
HANDLE file = CreateFileA(argv[1], 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 = fopen(argv[1], "r" );
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fileSize = ftell(f);
|
||||
fclose(f);
|
||||
|
||||
struct stat sb;
|
||||
char *p;
|
||||
int fd;
|
||||
|
||||
fd = open (argv[1], O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror ("open");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fstat (fd, &sb) == -1) {
|
||||
perror ("fstat");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!S_ISREG (sb.st_mode)) {
|
||||
fprintf (stderr, "%s is not a file\n", "lineitem.tbl");
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = (char*)mmap (0, fileSize, PROT_READ, MAP_SHARED, fd, 0);
|
||||
|
||||
if (p == MAP_FAILED) {
|
||||
perror ("mmap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (close (fd) == -1) {
|
||||
perror ("close");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("fsize: %lu\n", fileSize);
|
||||
#endif
|
||||
|
||||
parse(p, fileSize, req_num_threads);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
#endif // TINOBJ_LOADER_OPT_H_
|
||||
@@ -27,7 +27,9 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "trackball.h"
|
||||
#include "optimized-parse.cc"
|
||||
|
||||
#define TINYOBJ_LOADER_OPT_IMPLEMENTATION
|
||||
#include "tinyobj_loader_opt.h"
|
||||
|
||||
typedef struct {
|
||||
GLuint vb; // vertex buffer
|
||||
@@ -215,12 +217,8 @@ 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)
|
||||
{
|
||||
#if 1
|
||||
std::vector<float, lt::allocator<float>> vertices;
|
||||
std::vector<float, lt::allocator<float>> normals;
|
||||
std::vector<float, lt::allocator<float>> texcoords;
|
||||
std::vector<vertex_index, lt::allocator<vertex_index>> faces;
|
||||
std::vector<int, lt::allocator<int>> material_ids;
|
||||
std::vector<shape_t> shapes;
|
||||
tinyobj_opt::attrib_t attrib;
|
||||
std::vector<tinyobj_opt::shape_t> shapes;
|
||||
|
||||
size_t data_len = 0;
|
||||
const char* data = get_file_data(&data_len, filename);
|
||||
@@ -229,7 +227,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
return false;
|
||||
}
|
||||
printf("filesize: %d\n", (int)data_len);
|
||||
bool ret = parse(vertices, normals, texcoords, faces, material_ids, shapes, data, data_len, num_threads);
|
||||
bool ret = parseObj(&attrib, &shapes, data, data_len, num_threads);
|
||||
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
@@ -237,11 +235,13 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
{
|
||||
DrawObject o;
|
||||
std::vector<float> vb; // pos(3float), normal(3float), color(3float)
|
||||
for (size_t f = 0; f < faces.size()/3; f++) {
|
||||
|
||||
vertex_index idx0 = faces[3*f+0];
|
||||
vertex_index idx1 = faces[3*f+1];
|
||||
vertex_index idx2 = faces[3*f+2];
|
||||
size_t face_offset = 0;
|
||||
for (size_t v = 0; v < attrib.face_num_verts.size(); v++) {
|
||||
assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face.
|
||||
for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) {
|
||||
tinyobj_opt::vertex_index idx0 = attrib.faces[face_offset+3*f+0];
|
||||
tinyobj_opt::vertex_index idx1 = attrib.faces[face_offset+3*f+1];
|
||||
tinyobj_opt::vertex_index idx2 = attrib.faces[face_offset+3*f+2];
|
||||
|
||||
float v[3][3];
|
||||
for (int k = 0; k < 3; k++) {
|
||||
@@ -252,9 +252,9 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
|
||||
v[0][k] = vertices[3*f0+k];
|
||||
v[1][k] = vertices[3*f1+k];
|
||||
v[2][k] = vertices[3*f2+k];
|
||||
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]);
|
||||
@@ -265,20 +265,20 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
|
||||
float n[3][3];
|
||||
|
||||
if (normals.size() > 0) {
|
||||
if (attrib.normals.size() > 0) {
|
||||
int f0 = idx0.vn_idx;
|
||||
int f1 = idx1.vn_idx;
|
||||
int f2 = idx2.vn_idx;
|
||||
assert(f0 >= 0);
|
||||
assert(f1 >= 0);
|
||||
assert(f2 >= 0);
|
||||
assert(3*f0+2 < normals.size());
|
||||
assert(3*f1+2 < normals.size());
|
||||
assert(3*f2+2 < normals.size());
|
||||
assert(3*f0+2 < attrib.normals.size());
|
||||
assert(3*f1+2 < attrib.normals.size());
|
||||
assert(3*f2+2 < attrib.normals.size());
|
||||
for (int k = 0; k < 3; k++) {
|
||||
n[0][k] = normals[3*f0+k];
|
||||
n[1][k] = normals[3*f1+k];
|
||||
n[2][k] = normals[3*f2+k];
|
||||
n[0][k] = attrib.normals[3*f0+k];
|
||||
n[1][k] = attrib.normals[3*f1+k];
|
||||
n[2][k] = attrib.normals[3*f2+k];
|
||||
}
|
||||
} else {
|
||||
// compute geometric normal
|
||||
@@ -308,7 +308,8 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int n
|
||||
vb.push_back(c[1] * 0.5 + 0.5);
|
||||
vb.push_back(c[2] * 0.5 + 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
face_offset += attrib.face_num_verts[v];
|
||||
}
|
||||
|
||||
o.vb = 0;
|
||||
@@ -512,12 +513,8 @@ int main(int argc, char **argv)
|
||||
|
||||
if (benchmark_only) {
|
||||
|
||||
std::vector<float, lt::allocator<float>> vertices;
|
||||
std::vector<float, lt::allocator<float>> normals;
|
||||
std::vector<float, lt::allocator<float>> texcoords;
|
||||
std::vector<vertex_index, lt::allocator<vertex_index>> faces;
|
||||
std::vector<int, lt::allocator<int>> material_ids;
|
||||
std::vector<shape_t> shapes;
|
||||
tinyobj_opt::attrib_t attrib;
|
||||
std::vector<tinyobj_opt::shape_t> shapes;
|
||||
|
||||
size_t data_len = 0;
|
||||
const char* data = get_file_data(&data_len, argv[1]);
|
||||
@@ -526,7 +523,7 @@ int main(int argc, char **argv)
|
||||
return false;
|
||||
}
|
||||
printf("filesize: %d\n", (int)data_len);
|
||||
bool ret = parse(vertices, normals, texcoords, faces, material_ids, shapes, data, data_len, num_threads);
|
||||
bool ret = parseObj(&attrib, &shapes, data, data_len, num_threads);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user