Skip to content

Commit 88ad575

Browse files
committed
Initial support of parsing vertex color(extension format)
1 parent 3a9483c commit 88ad575

File tree

4 files changed

+118
-1
lines changed

4 files changed

+118
-1
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ TinyObjLoader is successfully used in ...
8989

9090
* Group(parse multiple group name)
9191
* Vertex
92+
* Vertex color(as an extension: https://blender.stackexchange.com/questions/31997/how-can-i-get-vertex-painted-obj-files-to-import-into-blender)
9293
* Texcoord
9394
* Normal
9495
* Material
@@ -139,6 +140,13 @@ attrib_t::texcoords => 2 floats per vertex
139140
| u | v | u | v | u | v | u | v | .... | u | v |
140141
+-----------+-----------+-----------+-----------+ +-----------+
141142
143+
attrib_t::colors => 3 floats per vertex(vertex color. optional)
144+
145+
c[0] c[1] c[2] c[3] c[n-1]
146+
+-----------+-----------+-----------+-----------+ +-----------+
147+
| x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z |
148+
+-----------+-----------+-----------+-----------+ +-----------+
149+
142150
```
143151

144152
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++) {
228236
tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2];
229237
tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0];
230238
tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1];
239+
// Optional: vertex colors
240+
// tinyobj::real_t red = attrib.colors[3*idx.vertex_index+0];
241+
// tinyobj::real_t green = attrib.colors[3*idx.vertex_index+1];
242+
// tinyobj::real_t blue = attrib.colors[3*idx.vertex_index+2];
231243
}
232244
index_offset += fv;
233245

models/cube-vertexcol.obj

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
mtllib cube.mtl
2+
3+
v 0.000000 2.000000 2.000000 0 0 0
4+
v 0.000000 0.000000 2.000000 0 0 1
5+
v 2.000000 0.000000 2.000000 0 1 0
6+
v 2.000000 2.000000 2.000000 0 1 1
7+
v 0.000000 2.000000 0.000000 1 0 0
8+
v 0.000000 0.000000 0.000000 1 0 1
9+
v 2.000000 0.000000 0.000000 1 1 0
10+
v 2.000000 2.000000 0.000000 1 1 1
11+
# 8 vertices
12+
13+
g front cube
14+
usemtl white
15+
f 1 2 3 4
16+
g back cube
17+
# expects white material
18+
f 8 7 6 5
19+
g right cube
20+
usemtl red
21+
f 4 3 7 8
22+
g top cube
23+
usemtl white
24+
f 5 1 4 8
25+
g left cube
26+
usemtl green
27+
f 5 6 2 1
28+
g bottom cube
29+
usemtl white
30+
f 2 6 7 3
31+
# 6 elements

tests/tester.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,39 @@ TEST_CASE("g_ignored", "[Issue138]") {
625625

626626
}
627627

628+
TEST_CASE("vertex-col-ext", "[Issue144]") {
629+
tinyobj::attrib_t attrib;
630+
std::vector<tinyobj::shape_t> shapes;
631+
std::vector<tinyobj::material_t> materials;
632+
633+
std::string err;
634+
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube-vertexcol.obj", gMtlBasePath);
635+
636+
if (!err.empty()) {
637+
std::cerr << err << std::endl;
638+
}
639+
640+
PrintInfo(attrib, shapes, materials);
641+
642+
REQUIRE(true == ret);
643+
REQUIRE((8 * 3) == attrib.colors.size());
644+
645+
REQUIRE(0 == Approx(attrib.colors[3 * 0 + 0]));
646+
REQUIRE(0 == Approx(attrib.colors[3 * 0 + 1]));
647+
REQUIRE(0 == Approx(attrib.colors[3 * 0 + 2]));
648+
649+
REQUIRE(0 == Approx(attrib.colors[3 * 1 + 0]));
650+
REQUIRE(0 == Approx(attrib.colors[3 * 1 + 1]));
651+
REQUIRE(1 == Approx(attrib.colors[3 * 1 + 2]));
652+
653+
REQUIRE(1 == Approx(attrib.colors[3 * 4 + 0]));
654+
655+
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 0]));
656+
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 1]));
657+
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 2]));
658+
659+
}
660+
628661
#if 0
629662
int
630663
main(

tiny_obj_loader.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ THE SOFTWARE.
2323
*/
2424

2525
//
26+
// version 1.1.0 : Support parsing vertex color(#144)
2627
// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
2728
// version 1.0.7 : Support multiple tex options(#126)
2829
// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124)
@@ -230,6 +231,7 @@ typedef struct {
230231
std::vector<real_t> vertices; // 'v'
231232
std::vector<real_t> normals; // 'vn'
232233
std::vector<real_t> texcoords; // 'vt'
234+
std::vector<real_t> colors; // extension: vertex colors
233235
} attrib_t;
234236

235237
typedef struct callback_t_ {
@@ -602,6 +604,19 @@ static inline real_t parseReal(const char **token, double default_value = 0.0) {
602604
return f;
603605
}
604606

607+
static inline bool parseReal(const char **token, real_t *out) {
608+
(*token) += strspn((*token), " \t");
609+
const char *end = (*token) + strcspn((*token), " \t\r");
610+
double val;
611+
bool ret = tryParseDouble((*token), end, &val);
612+
if (ret) {
613+
real_t f = static_cast<real_t>(val);
614+
(*out) = f;
615+
}
616+
(*token) = end;
617+
return ret;
618+
}
619+
605620
static inline void parseReal2(real_t *x, real_t *y, const char **token,
606621
const double default_x = 0.0,
607622
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,
629644
(*w) = parseReal(token, default_w);
630645
}
631646

647+
// Extension: parse vertex with colors(6 items)
648+
static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, real_t *r,
649+
real_t *g, real_t *b,
650+
const char **token, const double default_x = 0.0,
651+
const double default_y = 0.0,
652+
const double default_z = 0.0) {
653+
(*x) = parseReal(token, default_x);
654+
(*y) = parseReal(token, default_y);
655+
(*z) = parseReal(token, default_z);
656+
657+
(*r) = parseReal(token, 1.0);
658+
(*g) = parseReal(token, 1.0);
659+
(*b) = parseReal(token, 1.0);
660+
661+
return true;
662+
}
663+
632664
static inline bool parseOnOff(const char **token, bool default_value = true) {
633665
(*token) += strspn((*token), " \t");
634666
const char *end = (*token) + strcspn((*token), " \t\r");
@@ -1421,6 +1453,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
14211453
attrib->vertices.clear();
14221454
attrib->normals.clear();
14231455
attrib->texcoords.clear();
1456+
attrib->colors.clear();
14241457
shapes->clear();
14251458

14261459
std::stringstream errss;
@@ -1453,6 +1486,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
14531486
std::vector<real_t> v;
14541487
std::vector<real_t> vn;
14551488
std::vector<real_t> vt;
1489+
std::vector<real_t> vc;
14561490
std::vector<tag_t> tags;
14571491
std::vector<std::vector<vertex_index> > faceGroup;
14581492
std::string name;
@@ -1495,10 +1529,15 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
14951529
if (token[0] == 'v' && IS_SPACE((token[1]))) {
14961530
token += 2;
14971531
real_t x, y, z;
1498-
parseReal3(&x, &y, &z, &token);
1532+
real_t r, g, b;
1533+
parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token);
14991534
v.push_back(x);
15001535
v.push_back(y);
15011536
v.push_back(z);
1537+
1538+
vc.push_back(r);
1539+
vc.push_back(g);
1540+
vc.push_back(b);
15021541
continue;
15031542
}
15041543

@@ -1733,6 +1772,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
17331772
attrib->vertices.swap(v);
17341773
attrib->normals.swap(vn);
17351774
attrib->texcoords.swap(vt);
1775+
attrib->colors.swap(vc);
17361776

17371777
return true;
17381778
}
@@ -1785,6 +1825,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
17851825
// vertex
17861826
if (token[0] == 'v' && IS_SPACE((token[1]))) {
17871827
token += 2;
1828+
// TODO(syoyo): Support parsing vertex color extension.
17881829
real_t x, y, z, w; // w is optional. default = 1.0
17891830
parseV(&x, &y, &z, &w, &token);
17901831
if (callback.vertex_cb) {

0 commit comments

Comments
 (0)