Merge branch 'master' of github.com:syoyo/tinyobjloader
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
//
|
//
|
||||||
// Stiches multiple .obj files into one .obj.
|
// Stiches multiple .obj files into one .obj.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#define TINYOBJLOADER_IMPLEMENTATION
|
||||||
#include "../../tiny_obj_loader.h"
|
#include "../../tiny_obj_loader.h"
|
||||||
#include "obj_writer.h"
|
#include "obj_writer.h"
|
||||||
|
|
||||||
@@ -11,27 +13,59 @@
|
|||||||
|
|
||||||
typedef std::vector<tinyobj::shape_t> Shape;
|
typedef std::vector<tinyobj::shape_t> Shape;
|
||||||
typedef std::vector<tinyobj::material_t> Material;
|
typedef std::vector<tinyobj::material_t> Material;
|
||||||
|
typedef tinyobj::attrib_t Attribute;
|
||||||
|
|
||||||
void
|
void
|
||||||
StichObjs(
|
StichObjs(
|
||||||
|
tinyobj::attrib_t& out_attribute,
|
||||||
std::vector<tinyobj::shape_t>& out_shape,
|
std::vector<tinyobj::shape_t>& out_shape,
|
||||||
std::vector<tinyobj::material_t>& out_material,
|
std::vector<tinyobj::material_t>& out_material,
|
||||||
|
const std::vector<Attribute>& attributes,
|
||||||
const std::vector<Shape>& shapes,
|
const std::vector<Shape>& shapes,
|
||||||
const std::vector<Material>& materials)
|
const std::vector<Material>& materials)
|
||||||
{
|
{
|
||||||
int numShapes = 0;
|
// The amount of attributes, shape-vectors and material-vecotrs should be the same.
|
||||||
for (size_t i = 0; i < shapes.size(); i++) {
|
if(attributes.size() != shapes.size() && attributes.size() != materials.size()){
|
||||||
numShapes += (int)shapes[i].size();
|
std::cerr << "Size of attributes, shapes and Materials don't fit!" << attributes.size() << " " << shapes.size() <<" " << materials.size() << std::endl;;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int num_shapes = 0;
|
||||||
|
// 4 values (vertices, normals, texcoords, colors)
|
||||||
|
std::vector<int> num_attributes(4, 0);
|
||||||
|
int num_materials = 0;
|
||||||
|
for(int i = 0; i < shapes.size(); i++){
|
||||||
|
num_shapes += shapes[i].size();
|
||||||
|
}
|
||||||
|
for(int i = 0; i < attributes.size(); i++){
|
||||||
|
num_attributes[0] += attributes[i].vertices.size();
|
||||||
|
num_attributes[1] += attributes[i].normals.size();
|
||||||
|
num_attributes[2] += attributes[i].texcoords.size();
|
||||||
|
num_attributes[3] += attributes[i].colors.size();
|
||||||
|
}
|
||||||
|
for(int i = 0; i < materials.size(); i++){
|
||||||
|
num_materials += materials[i].size();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Total # of shapes = %d\n", numShapes);
|
// More performant, than push_back
|
||||||
int materialIdOffset = 0;
|
out_attribute.vertices.resize(num_attributes[0]);
|
||||||
|
out_attribute.normals.resize(num_attributes[1]);
|
||||||
|
out_attribute.texcoords.resize(num_attributes[2]);
|
||||||
|
out_attribute.colors.resize(num_attributes[3]);
|
||||||
|
out_shape.resize(num_shapes);
|
||||||
|
out_material.resize(num_materials);
|
||||||
|
|
||||||
size_t face_offset = 0;
|
int material_id_offset = 0;
|
||||||
|
int shape_id_offset = 0;
|
||||||
|
int vertex_idx_offset = 0;
|
||||||
|
int normal_idx_offset = 0;
|
||||||
|
int texcoord_idx_offset = 0;
|
||||||
|
int color_idx_offset = 0;
|
||||||
|
|
||||||
|
// shapes.size() = attributes.size() = materials.size()
|
||||||
for (size_t i = 0; i < shapes.size(); i++) {
|
for (size_t i = 0; i < shapes.size(); i++) {
|
||||||
|
|
||||||
|
// Copy shapes
|
||||||
for (size_t k = 0; k < shapes[i].size(); k++) {
|
for (size_t k = 0; k < shapes[i].size(); k++) {
|
||||||
|
|
||||||
std::string new_name = shapes[i][k].name;
|
std::string new_name = shapes[i][k].name;
|
||||||
// Add suffix
|
// Add suffix
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
@@ -39,36 +73,51 @@ StichObjs(
|
|||||||
new_name += std::string(buf);
|
new_name += std::string(buf);
|
||||||
|
|
||||||
printf("shape[%ld][%ld].name = %s\n", i, k, shapes[i][k].name.c_str());
|
printf("shape[%ld][%ld].name = %s\n", i, k, shapes[i][k].name.c_str());
|
||||||
assert((shapes[i][k].mesh.indices.size() % 3) == 0);
|
|
||||||
assert((shapes[i][k].mesh.positions.size() % 3) == 0);
|
|
||||||
|
|
||||||
tinyobj::shape_t new_shape = shapes[i][k];
|
tinyobj::shape_t new_shape = shapes[i][k];
|
||||||
// Add offset.
|
// Add material offset.
|
||||||
for (size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) {
|
for(size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) {
|
||||||
new_shape.mesh.material_ids[f] += materialIdOffset;
|
new_shape.mesh.material_ids[f] += material_id_offset;
|
||||||
|
}
|
||||||
|
// Add indices offset.
|
||||||
|
for(size_t f = 0; f < new_shape.mesh.indices.size(); f++){
|
||||||
|
tinyobj::index_t& ref = new_shape.mesh.indices[f];
|
||||||
|
if(ref.vertex_index > -1){
|
||||||
|
ref.vertex_index += vertex_idx_offset;
|
||||||
|
}
|
||||||
|
if(ref.normal_index > -1){
|
||||||
|
ref.normal_index += normal_idx_offset;
|
||||||
|
}
|
||||||
|
if(ref.texcoord_index > -1){
|
||||||
|
ref.texcoord_index += texcoord_idx_offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_shape.name = new_name;
|
new_shape.name = new_name;
|
||||||
printf("shape[%ld][%ld].new_name = %s\n", i, k, new_shape.name.c_str());
|
printf("shape[%ld][%ld].new_name = %s\n", i, k, new_shape.name.c_str());
|
||||||
|
|
||||||
out_shape.push_back(new_shape);
|
out_shape[shape_id_offset++] = new_shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
materialIdOffset += materials[i].size();
|
// Copy materials
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < materials.size(); i++) {
|
|
||||||
for (size_t k = 0; k < materials[i].size(); k++) {
|
for (size_t k = 0; k < materials[i].size(); k++) {
|
||||||
out_material.push_back(materials[i][k]);
|
out_material[material_id_offset++] = materials[i][k];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Copy attributes (3 floats per vertex, 3 floats per normal, 2 floats per texture-coordinate, 3 floats per color)
|
||||||
|
// You could also include a check here, if the sizes are dividable by 3 (resp. 2), but it's safe to simply assume, they do.
|
||||||
|
std::copy(attributes[i].vertices.begin(), attributes[i].vertices.end(), out_attribute.vertices.begin() + vertex_idx_offset * 3);
|
||||||
|
vertex_idx_offset += attributes[i].vertices.size() / 3;
|
||||||
|
std::copy(attributes[i].normals.begin(), attributes[i].normals.end(), out_attribute.normals.begin() + normal_idx_offset * 3);
|
||||||
|
normal_idx_offset += attributes[i].normals.size() / 3;
|
||||||
|
std::copy(attributes[i].texcoords.begin(), attributes[i].texcoords.end(), out_attribute.texcoords.begin() + texcoord_idx_offset * 2);
|
||||||
|
texcoord_idx_offset += attributes[i].texcoords.size() / 2;
|
||||||
|
std::copy(attributes[i].colors.begin(), attributes[i].colors.end(), out_attribute.colors.begin() + color_idx_offset);
|
||||||
|
color_idx_offset += attributes[i].colors.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int main(int argc, char **argv)
|
||||||
main(
|
|
||||||
int argc,
|
|
||||||
char **argv)
|
|
||||||
{
|
{
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
printf("Usage: obj_sticher input0.obj input1.obj ... output.obj\n");
|
printf("Usage: obj_sticher input0.obj input1.obj ... output.obj\n");
|
||||||
@@ -78,16 +127,15 @@ main(
|
|||||||
int num_objfiles = argc - 2;
|
int num_objfiles = argc - 2;
|
||||||
std::string out_filename = std::string(argv[argc-1]); // last element
|
std::string out_filename = std::string(argv[argc-1]); // last element
|
||||||
|
|
||||||
std::vector<Shape> shapes;
|
std::vector<Attribute> attributes(num_objfiles);
|
||||||
std::vector<Material> materials;
|
std::vector<Shape> shapes(num_objfiles);
|
||||||
shapes.resize(num_objfiles);
|
std::vector<Material> materials(num_objfiles);
|
||||||
materials.resize(num_objfiles);
|
|
||||||
|
|
||||||
for (int i = 0; i < num_objfiles; i++) {
|
for (int i = 0; i < num_objfiles; i++) {
|
||||||
std::cout << "Loading " << argv[i+1] << " ... " << std::flush;
|
std::cout << "Loading " << argv[i+1] << " ... " << std::flush;
|
||||||
|
|
||||||
std::string err;
|
std::string err;
|
||||||
bool ret = tinyobj::LoadObj(shapes[i], materials[i], err, argv[i+1]);
|
bool ret = tinyobj::LoadObj(&attributes[i], &shapes[i], &materials[i], &err, argv[i+1]);
|
||||||
if (!err.empty()) {
|
if (!err.empty()) {
|
||||||
std::cerr << err << std::endl;
|
std::cerr << err << std::endl;
|
||||||
}
|
}
|
||||||
@@ -98,12 +146,13 @@ main(
|
|||||||
std::cout << "DONE." << std::endl;
|
std::cout << "DONE." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<tinyobj::shape_t> out_shape;
|
Attribute out_attribute;
|
||||||
std::vector<tinyobj::material_t> out_material;
|
Shape out_shape;
|
||||||
StichObjs(out_shape, out_material, shapes, materials);
|
Material out_material;
|
||||||
|
StichObjs(out_attribute, out_shape, out_material, attributes, shapes, materials);
|
||||||
|
|
||||||
bool coordTransform = true;
|
bool coordTransform = true;
|
||||||
bool ret = WriteObj(out_filename, out_shape, out_material, coordTransform);
|
bool ret = WriteObj(out_filename, out_attribute, out_shape, out_material, coordTransform);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ bool WriteMat(const std::string& filename, const std::vector<tinyobj::material_t
|
|||||||
fprintf(fp, "Ke %f %f %f\n", mat.emission[0], mat.emission[1], mat.emission[2]);
|
fprintf(fp, "Ke %f %f %f\n", mat.emission[0], mat.emission[1], mat.emission[2]);
|
||||||
fprintf(fp, "Ns %f\n", mat.shininess);
|
fprintf(fp, "Ns %f\n", mat.shininess);
|
||||||
fprintf(fp, "Ni %f\n", mat.ior);
|
fprintf(fp, "Ni %f\n", mat.ior);
|
||||||
|
fprintf(fp, "illum %d\n", mat.illum);
|
||||||
|
fprintf(fp, "\n");
|
||||||
// @todo { texture }
|
// @todo { texture }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ bool WriteMat(const std::string& filename, const std::vector<tinyobj::material_t
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform) {
|
bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform) {
|
||||||
FILE* fp = fopen(filename.c_str(), "w");
|
FILE* fp = fopen(filename.c_str(), "w");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str());
|
fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str());
|
||||||
@@ -48,17 +50,53 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
|
|||||||
std::string basename = GetFileBasename(filename);
|
std::string basename = GetFileBasename(filename);
|
||||||
std::string material_filename = basename + ".mtl";
|
std::string material_filename = basename + ".mtl";
|
||||||
|
|
||||||
int v_offset = 0;
|
|
||||||
int vn_offset = 0;
|
|
||||||
int vt_offset = 0;
|
|
||||||
int prev_material_id = -1;
|
int prev_material_id = -1;
|
||||||
|
|
||||||
fprintf(fp, "mtllib %s\n", material_filename.c_str());
|
fprintf(fp, "mtllib %s\n\n", material_filename.c_str());
|
||||||
|
|
||||||
|
// facevarying vtx
|
||||||
|
for (size_t k = 0; k < attributes.vertices.size(); k+=3) {
|
||||||
|
if (coordTransform) {
|
||||||
|
fprintf(fp, "v %f %f %f\n",
|
||||||
|
attributes.vertices[k + 0],
|
||||||
|
attributes.vertices[k + 2],
|
||||||
|
-attributes.vertices[k + 1]);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "v %f %f %f\n",
|
||||||
|
attributes.vertices[k + 0],
|
||||||
|
attributes.vertices[k + 1],
|
||||||
|
attributes.vertices[k + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
|
||||||
|
// facevarying normal
|
||||||
|
for (size_t k = 0; k < attributes.normals.size(); k += 3) {
|
||||||
|
if (coordTransform) {
|
||||||
|
fprintf(fp, "vn %f %f %f\n",
|
||||||
|
attributes.normals[k + 0],
|
||||||
|
attributes.normals[k + 2],
|
||||||
|
-attributes.normals[k + 1]);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "vn %f %f %f\n",
|
||||||
|
attributes.normals[k + 0],
|
||||||
|
attributes.normals[k + 1],
|
||||||
|
attributes.normals[k + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
|
||||||
|
// facevarying texcoord
|
||||||
|
for (size_t k = 0; k < attributes.texcoords.size(); k += 2) {
|
||||||
|
fprintf(fp, "vt %f %f\n",
|
||||||
|
attributes.texcoords[k + 0],
|
||||||
|
attributes.texcoords[k + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < shapes.size(); i++) {
|
for (size_t i = 0; i < shapes.size(); i++) {
|
||||||
|
fprintf(fp, "\n");
|
||||||
bool has_vn = false;
|
|
||||||
bool has_vt = false;
|
|
||||||
|
|
||||||
if (shapes[i].name.empty()) {
|
if (shapes[i].name.empty()) {
|
||||||
fprintf(fp, "g Unknown\n");
|
fprintf(fp, "g Unknown\n");
|
||||||
@@ -66,101 +104,53 @@ bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>&
|
|||||||
fprintf(fp, "g %s\n", shapes[i].name.c_str());
|
fprintf(fp, "g %s\n", shapes[i].name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!shapes[i].material.name.empty()) {
|
bool has_vn = false;
|
||||||
// fprintf(fp, "usemtl %s\n", shapes[i].material.name.c_str());
|
bool has_vt = false;
|
||||||
//}
|
// Assumes normals and textures are set shape-wise.
|
||||||
|
if(shapes[i].mesh.indices.size() > 0){
|
||||||
// facevarying vtx
|
has_vn = shapes[i].mesh.indices[0].normal_index != -1;
|
||||||
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
|
has_vt = shapes[i].mesh.indices[0].texcoord_index != -1;
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
int idx = shapes[i].mesh.indices[3*k+j];
|
|
||||||
if (coordTransform) {
|
|
||||||
fprintf(fp, "v %f %f %f\n",
|
|
||||||
shapes[i].mesh.positions[3*idx+0],
|
|
||||||
shapes[i].mesh.positions[3*idx+2],
|
|
||||||
-shapes[i].mesh.positions[3*idx+1]);
|
|
||||||
} else {
|
|
||||||
fprintf(fp, "v %f %f %f\n",
|
|
||||||
shapes[i].mesh.positions[3*idx+0],
|
|
||||||
shapes[i].mesh.positions[3*idx+1],
|
|
||||||
shapes[i].mesh.positions[3*idx+2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// facevarying normal
|
|
||||||
if (shapes[i].mesh.normals.size() > 0) {
|
|
||||||
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
int idx = shapes[i].mesh.indices[3*k+j];
|
|
||||||
if (coordTransform) {
|
|
||||||
fprintf(fp, "vn %f %f %f\n",
|
|
||||||
shapes[i].mesh.normals[3*idx+0],
|
|
||||||
shapes[i].mesh.normals[3*idx+2],
|
|
||||||
-shapes[i].mesh.normals[3*idx+1]);
|
|
||||||
} else {
|
|
||||||
fprintf(fp, "vn %f %f %f\n",
|
|
||||||
shapes[i].mesh.normals[3*idx+0],
|
|
||||||
shapes[i].mesh.normals[3*idx+1],
|
|
||||||
shapes[i].mesh.normals[3*idx+2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shapes[i].mesh.normals.size() > 0) has_vn = true;
|
|
||||||
|
|
||||||
// facevarying texcoord
|
|
||||||
if (shapes[i].mesh.texcoords.size() > 0) {
|
|
||||||
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
int idx = shapes[i].mesh.indices[3*k+j];
|
|
||||||
fprintf(fp, "vt %f %f\n",
|
|
||||||
shapes[i].mesh.texcoords[2*idx+0],
|
|
||||||
shapes[i].mesh.texcoords[2*idx+1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shapes[i].mesh.texcoords.size() > 0) has_vt = true;
|
|
||||||
|
|
||||||
// face
|
// face
|
||||||
for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) {
|
int face_index = 0;
|
||||||
|
for (size_t k = 0; k < shapes[i].mesh.indices.size(); k += shapes[i].mesh.num_face_vertices[face_index++]) {
|
||||||
// Face index is 1-base.
|
// Check Materials
|
||||||
//int v0 = shapes[i].mesh.indices[3*k+0] + 1 + v_offset;
|
int material_id = shapes[i].mesh.material_ids[face_index];
|
||||||
//int v1 = shapes[i].mesh.indices[3*k+1] + 1 + v_offset;
|
|
||||||
//int v2 = shapes[i].mesh.indices[3*k+2] + 1 + v_offset;
|
|
||||||
int v0 = (3*k + 0) + 1 + v_offset;
|
|
||||||
int v1 = (3*k + 1) + 1 + v_offset;
|
|
||||||
int v2 = (3*k + 2) + 1 + v_offset;
|
|
||||||
|
|
||||||
int vt0 = (3*k + 0) + 1 + vt_offset;
|
|
||||||
int vt1 = (3*k + 1) + 1 + vt_offset;
|
|
||||||
int vt2 = (3*k + 2) + 1 + vt_offset;
|
|
||||||
|
|
||||||
int material_id = shapes[i].mesh.material_ids[k];
|
|
||||||
if (material_id != prev_material_id) {
|
if (material_id != prev_material_id) {
|
||||||
std::string material_name = materials[material_id].name;
|
std::string material_name = materials[material_id].name;
|
||||||
fprintf(fp, "usemtl %s\n", material_name.c_str());
|
fprintf(fp, "usemtl %s\n", material_name.c_str());
|
||||||
prev_material_id = material_id;
|
prev_material_id = material_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_vn && has_vt) {
|
unsigned char v_per_f = shapes[i].mesh.num_face_vertices[face_index];
|
||||||
fprintf(fp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
|
// Imperformant, but if you want to have variable vertices per face, you need some kind of a dynamic loop.
|
||||||
v0, vt0, v0, v1, vt1, v1, v2, vt2, v2);
|
fprintf(fp, "f");
|
||||||
} else if (has_vn && !has_vt) {
|
for(int l = 0; l < v_per_f; l++){
|
||||||
fprintf(fp, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2);
|
const tinyobj::index_t& ref = shapes[i].mesh.indices[k + l];
|
||||||
} else if (!has_vn && has_vt) {
|
if(has_vn && has_vt){
|
||||||
fprintf(fp, "f %d/%d %d/%d %d/%d\n", v0, v0, v1, v1, v2, v2);
|
// v0/t0/vn0
|
||||||
} else {
|
fprintf(fp, " %d/%d/%d", ref.vertex_index + 1, ref.texcoord_index + 1, ref.normal_index + 1);
|
||||||
fprintf(fp, "f %d %d %d\n", v0, v1, v2);
|
continue;
|
||||||
|
}
|
||||||
|
if(has_vn && !has_vt){
|
||||||
|
// v0//vn0
|
||||||
|
fprintf(fp, " %d//%d", ref.vertex_index + 1, ref.normal_index + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!has_vn && has_vt){
|
||||||
|
// v0/vt0
|
||||||
|
fprintf(fp, " %d/%d", ref.vertex_index + 1, ref.texcoord_index + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!has_vn && !has_vt){
|
||||||
|
// v0 v1 v2
|
||||||
|
fprintf(fp, " %d", ref.vertex_index + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
v_offset += shapes[i].mesh.indices.size();
|
|
||||||
//vn_offset += shapes[i].mesh.normals.size() / 3;
|
|
||||||
vt_offset += shapes[i].mesh.texcoords.size() / 2;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "../../tiny_obj_loader.h"
|
#include "../../tiny_obj_loader.h"
|
||||||
|
|
||||||
extern bool WriteObj(const std::string& filename, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform = false);
|
extern bool WriteObj(const std::string& filename, const tinyobj::attrib_t& attributes, const std::vector<tinyobj::shape_t>& shapes, const std::vector<tinyobj::material_t>& materials, bool coordTransform = false);
|
||||||
|
|
||||||
|
|
||||||
#endif // __OBJ_WRITER_H__
|
#endif // __OBJ_WRITER_H__
|
||||||
|
|||||||
@@ -6,11 +6,21 @@
|
|||||||
|
|
||||||
* C++-11 compiler
|
* C++-11 compiler
|
||||||
|
|
||||||
|
## How to build
|
||||||
|
|
||||||
|
```
|
||||||
|
$ premak5 gmake
|
||||||
|
```
|
||||||
|
|
||||||
## Compile options
|
## Compile options
|
||||||
|
|
||||||
* zstd compressed .obj support. `--with-zstd` premake option.
|
* zstd compressed .obj support. `--with-zstd` premake option.
|
||||||
* gzip compressed .obj support. `--with-zlib` premake option.
|
* gzip compressed .obj support. `--with-zlib` premake option.
|
||||||
|
|
||||||
|
## Notes on AMD GPU + Linux
|
||||||
|
|
||||||
|
You may need to link with libdrm(`-ldrm`).
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
* lfpAlloc : MIT license.
|
* lfpAlloc : MIT license.
|
||||||
|
|||||||
@@ -1306,15 +1306,17 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
end_idx = len - 1;
|
end_idx = len - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// true if the line currently read must be added to the current line info
|
||||||
|
bool new_line_found = (t == 0) || is_line_ending(buf, start_idx - 1, end_idx);
|
||||||
|
|
||||||
size_t prev_pos = start_idx;
|
size_t prev_pos = start_idx;
|
||||||
for (size_t i = start_idx; i < end_idx; i++) {
|
for (size_t i = start_idx; i < end_idx; i++) {
|
||||||
if (is_line_ending(buf, i, end_idx)) {
|
if (is_line_ending(buf, i, end_idx)) {
|
||||||
if ((t > 0) && (prev_pos == start_idx) &&
|
if (!new_line_found) {
|
||||||
(!is_line_ending(buf, start_idx - 1, end_idx))) {
|
|
||||||
// first linebreak found in (chunk > 0), and a line before this
|
// first linebreak found in (chunk > 0), and a line before this
|
||||||
// linebreak belongs to previous chunk, so skip it.
|
// linebreak belongs to previous chunk, so skip it.
|
||||||
prev_pos = i + 1;
|
prev_pos = i + 1;
|
||||||
continue;
|
new_line_found = true;
|
||||||
} else {
|
} else {
|
||||||
LineInfo info;
|
LineInfo info;
|
||||||
info.pos = prev_pos;
|
info.pos = prev_pos;
|
||||||
@@ -1329,11 +1331,10 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find extra line which spand across chunk boundary.
|
// If at least one line started in this chunk, find where it ends in the rest of the buffer
|
||||||
if ((t < num_threads) && (buf[end_idx - 1] != '\n')) {
|
if (new_line_found && (t < num_threads) && (buf[end_idx - 1] != '\n')) {
|
||||||
auto extra_span_idx = (std::min)(end_idx - 1 + chunk_size, len);
|
for (size_t i = end_idx; i < len; i++) {
|
||||||
for (size_t i = end_idx; i < extra_span_idx; i++) {
|
if (is_line_ending(buf, i, len)) {
|
||||||
if (is_line_ending(buf, i, extra_span_idx)) {
|
|
||||||
LineInfo info;
|
LineInfo info;
|
||||||
info.pos = prev_pos;
|
info.pos = prev_pos;
|
||||||
info.len = i - prev_pos;
|
info.len = i - prev_pos;
|
||||||
@@ -1493,7 +1494,7 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
attrib->texcoords.resize(num_vt * 2);
|
attrib->texcoords.resize(num_vt * 2);
|
||||||
attrib->indices.resize(num_f);
|
attrib->indices.resize(num_f);
|
||||||
attrib->face_num_verts.resize(num_indices);
|
attrib->face_num_verts.resize(num_indices);
|
||||||
attrib->material_ids.resize(num_indices);
|
attrib->material_ids.resize(num_indices, -1);
|
||||||
|
|
||||||
size_t v_offsets[kMaxThreads];
|
size_t v_offsets[kMaxThreads];
|
||||||
size_t n_offsets[kMaxThreads];
|
size_t n_offsets[kMaxThreads];
|
||||||
@@ -1524,22 +1525,43 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
size_t t_count = t_offsets[t];
|
size_t t_count = t_offsets[t];
|
||||||
size_t f_count = f_offsets[t];
|
size_t f_count = f_offsets[t];
|
||||||
size_t face_count = face_offsets[t];
|
size_t face_count = face_offsets[t];
|
||||||
int material_id = -1; // -1 = default unknown material.
|
|
||||||
|
|
||||||
for (size_t i = 0; i < commands[t].size(); i++) {
|
for (size_t i = 0; i < commands[t].size(); i++) {
|
||||||
if (commands[t][i].type == COMMAND_EMPTY) {
|
if (commands[t][i].type == COMMAND_EMPTY) {
|
||||||
continue;
|
continue;
|
||||||
} else if (commands[t][i].type == COMMAND_USEMTL) {
|
} else if (commands[t][i].type == COMMAND_USEMTL) {
|
||||||
if (commands[t][i].material_name &&
|
if (commands[t][i].material_name &&
|
||||||
commands[t][i].material_name_len > 0) {
|
commands[t][i].material_name_len > 0 &&
|
||||||
std::string material_name(commands[t][i].material_name,
|
// check if there are still faces after this command
|
||||||
commands[t][i].material_name_len);
|
face_count < num_indices) {
|
||||||
|
// Find next face
|
||||||
if (material_map.find(material_name) != material_map.end()) {
|
bool found = false;
|
||||||
material_id = material_map[material_name];
|
size_t i_start = i + 1, t_next, i_next;
|
||||||
} else {
|
for (t_next = t; t_next < num_threads; t_next++) {
|
||||||
// Assign invalid material ID
|
for (i_next = i_start; i_next < commands[t_next].size(); i_next++) {
|
||||||
material_id = -1;
|
if (commands[t_next][i_next].type == COMMAND_F) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
|
i_start = 0;
|
||||||
|
}
|
||||||
|
// Assign material to this face
|
||||||
|
if (found) {
|
||||||
|
std::string material_name(commands[t][i].material_name,
|
||||||
|
commands[t][i].material_name_len);
|
||||||
|
for (size_t k = 0; k < commands[t_next][i_next].f_num_verts.size(); k++) {
|
||||||
|
if (material_map.find(material_name) != material_map.end()) {
|
||||||
|
attrib->material_ids[face_count + k] = material_map[material_name];
|
||||||
|
} else {
|
||||||
|
// Assign invalid material ID
|
||||||
|
// Set a different value than the default, to
|
||||||
|
// prevent following faces from being assigned a valid material
|
||||||
|
attrib->material_ids[face_count + k] = -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (commands[t][i].type == COMMAND_V) {
|
} else if (commands[t][i].type == COMMAND_V) {
|
||||||
@@ -1566,7 +1588,6 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
index_t(vertex_index, texcoord_index, normal_index);
|
index_t(vertex_index, texcoord_index, normal_index);
|
||||||
}
|
}
|
||||||
for (size_t k = 0; k < commands[t][i].f_num_verts.size(); k++) {
|
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] =
|
attrib->face_num_verts[face_count + k] =
|
||||||
commands[t][i].f_num_verts[k];
|
commands[t][i].f_num_verts[k];
|
||||||
}
|
}
|
||||||
@@ -1581,19 +1602,13 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|||||||
for (size_t t = 0; t < workers->size(); t++) {
|
for (size_t t = 0; t < workers->size(); t++) {
|
||||||
workers[t].join();
|
workers[t].join();
|
||||||
}
|
}
|
||||||
if(material_map.size()>1&& num_threads>1) {
|
|
||||||
for (size_t t = 0; t < num_threads; t++) {
|
// To each face with uninitialized material id,
|
||||||
size_t face_count = face_offsets[t];
|
// assign the material id of the last face preceding it that has one
|
||||||
if (-1 == attrib->material_ids[face_count]) {
|
for (size_t face_count = 1; face_count < num_indices; ++face_count)
|
||||||
int prev_material_id = attrib->material_ids[face_count - 1];
|
if (attrib->material_ids[face_count] == -1)
|
||||||
size_t max_face_offset = (t == num_threads - 1) ? attrib->material_ids.size() : face_offsets[t + 1];
|
attrib->material_ids[face_count] = attrib->material_ids[face_count - 1];
|
||||||
for (int i = face_count; i<max_face_offset; ++i) {
|
|
||||||
if (attrib->material_ids[i] != -1) break;
|
|
||||||
attrib->material_ids[i] = prev_material_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto t_end = std::chrono::high_resolution_clock::now();
|
auto t_end = std::chrono::high_resolution_clock::now();
|
||||||
ms_merge = t_end - t_start;
|
ms_merge = t_end - t_start;
|
||||||
}
|
}
|
||||||
|
|||||||
24
models/invalid-face-definition.mtl
Normal file
24
models/invalid-face-definition.mtl
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
newmtl white
|
||||||
|
Ka 0 0 0
|
||||||
|
Kd 1 1 1
|
||||||
|
Ks 0 0 0
|
||||||
|
|
||||||
|
newmtl red
|
||||||
|
Ka 0 0 0
|
||||||
|
Kd 1 0 0
|
||||||
|
Ks 0 0 0
|
||||||
|
|
||||||
|
newmtl green
|
||||||
|
Ka 0 0 0
|
||||||
|
Kd 0 1 0
|
||||||
|
Ks 0 0 0
|
||||||
|
|
||||||
|
newmtl blue
|
||||||
|
Ka 0 0 0
|
||||||
|
Kd 0 0 1
|
||||||
|
Ks 0 0 0
|
||||||
|
|
||||||
|
newmtl light
|
||||||
|
Ka 20 20 20
|
||||||
|
Kd 1 1 1
|
||||||
|
Ks 0 0 0
|
||||||
18
models/invalid-face-definition.obj
Normal file
18
models/invalid-face-definition.obj
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
mtllib invalid-face-definition.mtl
|
||||||
|
|
||||||
|
v 0.000000 2.000000 2.000000
|
||||||
|
v 0.000000 0.000000 2.000000
|
||||||
|
v 2.000000 0.000000 2.000000
|
||||||
|
v 2.000000 2.000000 2.000000
|
||||||
|
v 0.000000 2.000000 0.000000
|
||||||
|
v 0.000000 0.000000 0.000000
|
||||||
|
v 2.000000 0.000000 0.000000
|
||||||
|
v 2.000000 2.000000 0.000000
|
||||||
|
# 8 vertices
|
||||||
|
|
||||||
|
g front cube
|
||||||
|
usemtl white
|
||||||
|
f 1
|
||||||
|
g back cube
|
||||||
|
# expects white material
|
||||||
|
f 8 7
|
||||||
@@ -751,6 +751,23 @@ TEST_CASE("smoothing-group", "[Issue162]") {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("invalid-face-definition", "[face]") {
|
||||||
|
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/invalid-face-definition.obj", gMtlBasePath);
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << "[face] " << err << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
REQUIRE(1 == shapes.size());
|
||||||
|
REQUIRE(0 == shapes[0].mesh.indices.size());
|
||||||
|
}
|
||||||
|
|
||||||
// Fuzzer test.
|
// Fuzzer test.
|
||||||
// Just check if it does not crash.
|
// Just check if it does not crash.
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ namespace tinyobj {
|
|||||||
#if __has_warning("-Wzero-as-null-pointer-constant")
|
#if __has_warning("-Wzero-as-null-pointer-constant")
|
||||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#pragma clang diagnostic ignored "-Wpadded"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ...
|
// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ...
|
||||||
@@ -370,6 +373,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -867,22 +871,22 @@ static bool ParseTextureNameAndOption(std::string *texname,
|
|||||||
} else {
|
} else {
|
||||||
texopt->imfchan = 'm';
|
texopt->imfchan = 'm';
|
||||||
}
|
}
|
||||||
texopt->bump_multiplier = 1.0f;
|
texopt->bump_multiplier = static_cast<real_t>(1.0);
|
||||||
texopt->clamp = false;
|
texopt->clamp = false;
|
||||||
texopt->blendu = true;
|
texopt->blendu = true;
|
||||||
texopt->blendv = true;
|
texopt->blendv = true;
|
||||||
texopt->sharpness = 1.0f;
|
texopt->sharpness = static_cast<real_t>(1.0);
|
||||||
texopt->brightness = 0.0f;
|
texopt->brightness = static_cast<real_t>(0.0);
|
||||||
texopt->contrast = 1.0f;
|
texopt->contrast = static_cast<real_t>(1.0);
|
||||||
texopt->origin_offset[0] = 0.0f;
|
texopt->origin_offset[0] = static_cast<real_t>(0.0);
|
||||||
texopt->origin_offset[1] = 0.0f;
|
texopt->origin_offset[1] = static_cast<real_t>(0.0);
|
||||||
texopt->origin_offset[2] = 0.0f;
|
texopt->origin_offset[2] = static_cast<real_t>(0.0);
|
||||||
texopt->scale[0] = 1.0f;
|
texopt->scale[0] = static_cast<real_t>(1.0);
|
||||||
texopt->scale[1] = 1.0f;
|
texopt->scale[1] = static_cast<real_t>(1.0);
|
||||||
texopt->scale[2] = 1.0f;
|
texopt->scale[2] = static_cast<real_t>(1.0);
|
||||||
texopt->turbulence[0] = 0.0f;
|
texopt->turbulence[0] = static_cast<real_t>(0.0);
|
||||||
texopt->turbulence[1] = 0.0f;
|
texopt->turbulence[1] = static_cast<real_t>(0.0);
|
||||||
texopt->turbulence[2] = 0.0f;
|
texopt->turbulence[2] = static_cast<real_t>(0.0);
|
||||||
texopt->type = TEXTURE_TYPE_NONE;
|
texopt->type = TEXTURE_TYPE_NONE;
|
||||||
|
|
||||||
const char *token = linebuf; // Assume line ends with NULL
|
const char *token = linebuf; // Assume line ends with NULL
|
||||||
@@ -968,24 +972,24 @@ static void InitMaterial(material_t *material) {
|
|||||||
material->reflection_texname = "";
|
material->reflection_texname = "";
|
||||||
material->alpha_texname = "";
|
material->alpha_texname = "";
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
material->ambient[i] = 0.f;
|
material->ambient[i] = static_cast<real_t>(0.0);
|
||||||
material->diffuse[i] = 0.f;
|
material->diffuse[i] = static_cast<real_t>(0.0);
|
||||||
material->specular[i] = 0.f;
|
material->specular[i] = static_cast<real_t>(0.0);
|
||||||
material->transmittance[i] = 0.f;
|
material->transmittance[i] = static_cast<real_t>(0.0);
|
||||||
material->emission[i] = 0.f;
|
material->emission[i] = static_cast<real_t>(0.0);
|
||||||
}
|
}
|
||||||
material->illum = 0;
|
material->illum = 0;
|
||||||
material->dissolve = 1.f;
|
material->dissolve = static_cast<real_t>(1.0);
|
||||||
material->shininess = 1.f;
|
material->shininess = static_cast<real_t>(1.0);
|
||||||
material->ior = 1.f;
|
material->ior = static_cast<real_t>(1.0);
|
||||||
|
|
||||||
material->roughness = 0.f;
|
material->roughness = static_cast<real_t>(0.0);
|
||||||
material->metallic = 0.f;
|
material->metallic = static_cast<real_t>(0.0);
|
||||||
material->sheen = 0.f;
|
material->sheen = static_cast<real_t>(0.0);
|
||||||
material->clearcoat_thickness = 0.f;
|
material->clearcoat_thickness = static_cast<real_t>(0.0);
|
||||||
material->clearcoat_roughness = 0.f;
|
material->clearcoat_roughness = static_cast<real_t>(0.0);
|
||||||
material->anisotropy_rotation = 0.f;
|
material->anisotropy_rotation = static_cast<real_t>(0.0);
|
||||||
material->anisotropy = 0.f;
|
material->anisotropy = static_cast<real_t>(0.0);
|
||||||
material->roughness_texname = "";
|
material->roughness_texname = "";
|
||||||
material->metallic_texname = "";
|
material->metallic_texname = "";
|
||||||
material->sheen_texname = "";
|
material->sheen_texname = "";
|
||||||
@@ -996,8 +1000,9 @@ static void InitMaterial(material_t *material) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
|
// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
|
||||||
static int pnpoly(int nvert, float *vertx, float *verty, float testx,
|
template<typename T>
|
||||||
float testy) {
|
static int pnpoly(int nvert, T *vertx, T *verty, T testx,
|
||||||
|
T testy) {
|
||||||
int i, j, c = 0;
|
int i, j, c = 0;
|
||||||
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
|
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
|
||||||
if (((verty[i] > testy) != (verty[j] > testy)) &&
|
if (((verty[i] > testy) != (verty[j] > testy)) &&
|
||||||
@@ -1027,7 +1032,7 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
|||||||
size_t npolys = face.vertex_indices.size();
|
size_t npolys = face.vertex_indices.size();
|
||||||
|
|
||||||
if (npolys < 3) {
|
if (npolys < 3) {
|
||||||
// ??? Invalid face definition.
|
// Face must have 3+ vertices.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1068,10 +1073,10 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
|||||||
real_t e1x = v2x - v1x;
|
real_t e1x = v2x - v1x;
|
||||||
real_t e1y = v2y - v1y;
|
real_t e1y = v2y - v1y;
|
||||||
real_t e1z = v2z - v1z;
|
real_t e1z = v2z - v1z;
|
||||||
float cx = std::fabs(e0y * e1z - e0z * e1y);
|
real_t cx = std::fabs(e0y * e1z - e0z * e1y);
|
||||||
float cy = std::fabs(e0z * e1x - e0x * e1z);
|
real_t cy = std::fabs(e0z * e1x - e0x * e1z);
|
||||||
float cz = std::fabs(e0x * e1y - e0y * e1x);
|
real_t cz = std::fabs(e0x * e1y - e0y * e1x);
|
||||||
const float epsilon = 0.0001f;
|
const real_t epsilon = std::numeric_limits<real_t>::epsilon();
|
||||||
if (cx > epsilon || cy > epsilon || cz > epsilon) {
|
if (cx > epsilon || cy > epsilon || cz > epsilon) {
|
||||||
// found a corner
|
// found a corner
|
||||||
if (cx > cy && cx > cz) {
|
if (cx > cy && cx > cz) {
|
||||||
@@ -1100,7 +1105,7 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
|||||||
real_t v0y = v[vi0 * 3 + axes[1]];
|
real_t v0y = v[vi0 * 3 + axes[1]];
|
||||||
real_t v1x = v[vi1 * 3 + axes[0]];
|
real_t v1x = v[vi1 * 3 + axes[0]];
|
||||||
real_t v1y = v[vi1 * 3 + axes[1]];
|
real_t v1y = v[vi1 * 3 + axes[1]];
|
||||||
area += (v0x * v1y - v0y * v1x) * 0.5f;
|
area += (v0x * v1y - v0y * v1x) * static_cast<real_t>(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxRounds =
|
int maxRounds =
|
||||||
@@ -1136,7 +1141,7 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
|||||||
real_t e1y = vy[2] - vy[1];
|
real_t e1y = vy[2] - vy[1];
|
||||||
real_t cross = e0x * e1y - e0y * e1x;
|
real_t cross = e0x * e1y - e0y * e1x;
|
||||||
// if an internal angle
|
// if an internal angle
|
||||||
if (cross * area < 0.0f) {
|
if (cross * area < static_cast<real_t>(0.0)) {
|
||||||
guess_vert += 1;
|
guess_vert += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1437,7 +1442,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|||||||
// We invert value of Tr(assume Tr is in range [0, 1])
|
// We invert value of Tr(assume Tr is in range [0, 1])
|
||||||
// NOTE: Interpretation of Tr is application(exporter) dependent. For
|
// NOTE: Interpretation of Tr is application(exporter) dependent. For
|
||||||
// some application(e.g. 3ds max obj exporter), Tr = d(Issue 43)
|
// some application(e.g. 3ds max obj exporter), Tr = d(Issue 43)
|
||||||
material.dissolve = 1.0f - parseReal(&token);
|
material.dissolve = static_cast<real_t>(1.0) - parseReal(&token);
|
||||||
}
|
}
|
||||||
has_tr = true;
|
has_tr = true;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user