From 88ad575f6247a215bda2a8938bc0f6e41d113ea4 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 12 Oct 2017 19:04:38 +0900 Subject: Initial support of parsing vertex color(extension format) --- README.md | 12 ++++++++++++ models/cube-vertexcol.obj | 31 +++++++++++++++++++++++++++++++ tests/tester.cc | 33 +++++++++++++++++++++++++++++++++ tiny_obj_loader.h | 43 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 models/cube-vertexcol.obj diff --git a/README.md b/README.md index 2737f08..8608c3b 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ TinyObjLoader is successfully used in ... * Group(parse multiple group name) * Vertex + * Vertex color(as an extension: https://blender.stackexchange.com/questions/31997/how-can-i-get-vertex-painted-obj-files-to-import-into-blender) * Texcoord * Normal * Material @@ -139,6 +140,13 @@ attrib_t::texcoords => 2 floats per vertex | u | v | u | v | u | v | u | v | .... | u | v | +-----------+-----------+-----------+-----------+ +-----------+ +attrib_t::colors => 3 floats per vertex(vertex color. optional) + + c[0] c[1] c[2] c[3] c[n-1] + +-----------+-----------+-----------+-----------+ +-----------+ + | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | + +-----------+-----------+-----------+-----------+ +-----------+ + ``` Each `shape_t::mesh_t` does not contain vertex data but contains array index to `attrib_t`. @@ -228,6 +236,10 @@ for (size_t s = 0; s < shapes.size(); s++) { tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2]; tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0]; tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1]; + // Optional: vertex colors + // tinyobj::real_t red = attrib.colors[3*idx.vertex_index+0]; + // tinyobj::real_t green = attrib.colors[3*idx.vertex_index+1]; + // tinyobj::real_t blue = attrib.colors[3*idx.vertex_index+2]; } index_offset += fv; diff --git a/models/cube-vertexcol.obj b/models/cube-vertexcol.obj new file mode 100644 index 0000000..494ce21 --- /dev/null +++ b/models/cube-vertexcol.obj @@ -0,0 +1,31 @@ +mtllib cube.mtl + +v 0.000000 2.000000 2.000000 0 0 0 +v 0.000000 0.000000 2.000000 0 0 1 +v 2.000000 0.000000 2.000000 0 1 0 +v 2.000000 2.000000 2.000000 0 1 1 +v 0.000000 2.000000 0.000000 1 0 0 +v 0.000000 0.000000 0.000000 1 0 1 +v 2.000000 0.000000 0.000000 1 1 0 +v 2.000000 2.000000 0.000000 1 1 1 +# 8 vertices + +g front cube +usemtl white +f 1 2 3 4 +g back cube +# expects white material +f 8 7 6 5 +g right cube +usemtl red +f 4 3 7 8 +g top cube +usemtl white +f 5 1 4 8 +g left cube +usemtl green +f 5 6 2 1 +g bottom cube +usemtl white +f 2 6 7 3 +# 6 elements diff --git a/tests/tester.cc b/tests/tester.cc index d4070c9..83b1df5 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -625,6 +625,39 @@ TEST_CASE("g_ignored", "[Issue138]") { } +TEST_CASE("vertex-col-ext", "[Issue144]") { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube-vertexcol.obj", gMtlBasePath); + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + PrintInfo(attrib, shapes, materials); + + REQUIRE(true == ret); + REQUIRE((8 * 3) == attrib.colors.size()); + + REQUIRE(0 == Approx(attrib.colors[3 * 0 + 0])); + REQUIRE(0 == Approx(attrib.colors[3 * 0 + 1])); + REQUIRE(0 == Approx(attrib.colors[3 * 0 + 2])); + + REQUIRE(0 == Approx(attrib.colors[3 * 1 + 0])); + REQUIRE(0 == Approx(attrib.colors[3 * 1 + 1])); + REQUIRE(1 == Approx(attrib.colors[3 * 1 + 2])); + + REQUIRE(1 == Approx(attrib.colors[3 * 4 + 0])); + + REQUIRE(1 == Approx(attrib.colors[3 * 7 + 0])); + REQUIRE(1 == Approx(attrib.colors[3 * 7 + 1])); + REQUIRE(1 == Approx(attrib.colors[3 * 7 + 2])); + +} + #if 0 int main( diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 4e2f6b5..3d02e8d 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -23,6 +23,7 @@ THE SOFTWARE. */ // +// version 1.1.0 : Support parsing vertex color(#144) // version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) // version 1.0.7 : Support multiple tex options(#126) // version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) @@ -230,6 +231,7 @@ typedef struct { std::vector vertices; // 'v' std::vector normals; // 'vn' std::vector texcoords; // 'vt' + std::vector colors; // extension: vertex colors } attrib_t; typedef struct callback_t_ { @@ -602,6 +604,19 @@ static inline real_t parseReal(const char **token, double default_value = 0.0) { return f; } +static inline bool parseReal(const char **token, real_t *out) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val; + bool ret = tryParseDouble((*token), end, &val); + if (ret) { + real_t f = static_cast(val); + (*out) = f; + } + (*token) = end; + return ret; +} + static inline void parseReal2(real_t *x, real_t *y, const char **token, const double default_x = 0.0, const double default_y = 0.0) { @@ -629,6 +644,23 @@ static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, (*w) = parseReal(token, default_w); } +// Extension: parse vertex with colors(6 items) +static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, real_t *r, + real_t *g, real_t *b, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + + (*r) = parseReal(token, 1.0); + (*g) = parseReal(token, 1.0); + (*b) = parseReal(token, 1.0); + + return true; +} + static inline bool parseOnOff(const char **token, bool default_value = true) { (*token) += strspn((*token), " \t"); const char *end = (*token) + strcspn((*token), " \t\r"); @@ -1421,6 +1453,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, attrib->vertices.clear(); attrib->normals.clear(); attrib->texcoords.clear(); + attrib->colors.clear(); shapes->clear(); std::stringstream errss; @@ -1453,6 +1486,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, std::vector v; std::vector vn; std::vector vt; + std::vector vc; std::vector tags; std::vector > faceGroup; std::string name; @@ -1495,10 +1529,15 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, if (token[0] == 'v' && IS_SPACE((token[1]))) { token += 2; real_t x, y, z; - parseReal3(&x, &y, &z, &token); + real_t r, g, b; + parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); v.push_back(x); v.push_back(y); v.push_back(z); + + vc.push_back(r); + vc.push_back(g); + vc.push_back(b); continue; } @@ -1733,6 +1772,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, attrib->vertices.swap(v); attrib->normals.swap(vn); attrib->texcoords.swap(vt); + attrib->colors.swap(vc); return true; } @@ -1785,6 +1825,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, // vertex if (token[0] == 'v' && IS_SPACE((token[1]))) { token += 2; + // TODO(syoyo): Support parsing vertex color extension. real_t x, y, z, w; // w is optional. default = 1.0 parseV(&x, &y, &z, &w, &token); if (callback.vertex_cb) { -- cgit v1.2.3