diff --git a/openvdb_cmd/vdb_tool/include/Geometry.h b/openvdb_cmd/vdb_tool/include/Geometry.h index 1415546aed..c1f4e4e4f1 100644 --- a/openvdb_cmd/vdb_tool/include/Geometry.h +++ b/openvdb_cmd/vdb_tool/include/Geometry.h @@ -647,7 +647,10 @@ void Geometry::readPLY(std::istream &is) return 0; }; + // check header if (!test(0, {"ply"})) error("vdb_tool::readPLY: not a ply file"); + + // check file format int format = -1;// 0 is ascii, 1 is little endian and 2 is big endian tokens = tokenize_line(); if (!(test(0, {"format"}) && test(2, {"1.0"})) ) { @@ -663,20 +666,18 @@ void Geometry::readPLY(std::istream &is) } const bool reverseBytes = format && format != (isLittleEndian() ? 1 : 2); // header: https://www.mathworks.com/help/vision/ug/the-ply-format.html - size_t vtxCount = 0, polyCount = 0, sizeOfVertex = 0, vertexStride=0; - struct Triplet {int offset, id, size; } xyz[3];// byte offset, id, size - struct Skip {int count, bytes;} vtx_skip[2]={{0,0},{0,0}}, ply_skip[2]={{0,0},{0,0}};// head, {x,y,z}, tail + size_t vtxCount = 0, faceCount = 0; + int vtxStride=0, vtxProps=0;// byte size of all vtx properties, number of vertex properties + struct Triplet {int offset, id, size;} xyz[3];// byte offset, id#, byte size + struct Skip {int count, bytes;} faceSkip[2]={{0,0},{0,0}};// head, {faces}, tail tokens = tokenize_line(); bool run = true; while(run) { if ( test(0, {"element"}) ) { if ( test(1, {"vertex"}) ) { vtxCount = std::stoll(tokens[2]); - int offset = 0, count = 0; - int n = 0; const std::string axis[3] = {"x", "y", "z"}; while(true) { - const int m = n>0 ? 1 : 0;// indicates before and after vertices tokens = tokenize_line(); if ( test(0, {"end_header"}) ) { run = false; @@ -684,56 +685,15 @@ void Geometry::readPLY(std::istream &is) } else if ( test(0, {"element"}) ) { break; } else if ( test(0, {"property"}) ) { -#ifdef MY_CLEAN_VERSION - Triplet t{offset, count++, sizeOf(1)}; + Triplet t{vtxStride, vtxProps++, sizeOf(1)}; for (int i=0; i<3; ++i) if (test(2, {axis[i]})) xyz[i] = t; - vertexStride = (offset += t.size); + vtxStride += t.size; } } for (int i=0; i<3; ++i) if (xyz[i].size!=4 && xyz[i].size!=8) error("vdb_tool::readPLY: missing "+axis[i]+ " vertex coordinates or unsupported size "+std::to_string(xyz[i].size)); -#else - if ( test(1, {"float", "float32"}) ) { - if ( test(2, {"x", "y", "z"}) ) {// vertex coordinates - if (sizeOfVertex == sizeof(double)) error("vdb_tool::readPLY: mixed float and double precision of vertices is not allowed"); - sizeOfVertex = sizeof(float); - if (n>2 || !test(2, {axis[n++]}) ) error("vdb_tool::readPLY: expected x or y or z"); - } else {// e.g. nx, ny, nz, intensity, s, t etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex float property interlaced with coordinates"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(float)); - } - } else if ( test(1, {"double", "float64"}) ) { - if ( test(2, {"x", "y", "z"}) ) {// vertex coordinates - if (sizeOfVertex == sizeof(float)) error("vdb_tool::readPLY: mixed float and double precision of vertices is not allowed"); - sizeOfVertex = sizeof(double); - if (n>2 || !test(2, {axis[n++]}) ) error("vdb_tool::readPLY: expected x or y or z"); - } else {// e.g. nx, ny, nz, intensity, s, t etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex float property interlaced with coordinates"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(double)); - } - } else if ( test(1, {"int16", "uint16"}) ) {// e.g. material_index etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex int16 property interlaced with coordinates is not supported"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(int16_t)); - } else if ( test(1, {"int", "int32"}) ) {// e.g. material_index etc - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex int32 property interlaced with coordinates is not supported"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(int32_t)); - } else if ( test(1, {"uchar", "int8"}) ) {// eg red, green, blue, alpha - if (n!=0 && n!=3) error("vdb_tool::readPLY: vertex int8 property interlaced with coordinates is not supported"); - vtx_skip[m].count += 1; - vtx_skip[m].bytes += static_cast(sizeof(unsigned char)); - } else { - error("vdb_tool::readPLY: invalid vertex property"); - } - } - } - if (n!=3) error("vdb_tool::readPLY: missing vertex coordinates"); -#endif } else if ( test(1, {"face"}) ) { - polyCount = std::stoll(tokens[2]); + faceCount = std::stoll(tokens[2]); int n = 0; while (true) { tokens = tokenize_line(); @@ -743,14 +703,14 @@ void Geometry::readPLY(std::istream &is) } else if (test(0, {"element"}) ) { break; } else if (test(0, {"property"}) ) { - if (test(1, {"list"}) && - test(2, {"uchar", "uint8"}) && - test(3, {"int", "uint", "int32"}) && + if (test(1, {"list"}) &&// list of vertex ID belonging to a polygon + test(2, {"uchar", "uint8"}) &&// size of polygon, e.g. 3 or 4 + test(3, {"int", "uint", "int32"}) &&// type of vertex id test(4, {"vertex_index", "vertex_indices"}) ) { n = 1; } else if ( test(1, {"uchar", "uint8"}) ) { - ply_skip[n].count += 1; - ply_skip[n].bytes += 1; + faceSkip[n].count += 1; + faceSkip[n].bytes += 1; } else { error("vdb_tool::readPLY: invalid face properties"); } @@ -779,133 +739,69 @@ void Geometry::readPLY(std::istream &is) // read vertex coordinates mVtx.resize(vtxCount); if (format) {// binary -#ifdef MY_CLEAN_VERSION - if (xyz[0].offset==0 && xyz[1].offset==4 && xyz[2].offset==8 && vertexStride==12) { + if (xyz[0].offset==0 && xyz[1].offset==4 && xyz[2].offset==8 && vtxStride==12) {// most common case is.read((char *)(mVtx.data()), vtxCount * 3 * sizeof(float)); + if (reverseBytes) for (Vec3f &v : mVtx) swapBytes(&v[0], 3); } else { - char *buffer = static_cast(std::malloc(vtxCount*vertexStride)), *p=buffer;// uninitialized + char *buffer = static_cast(std::malloc(vtxCount*vtxStride)), *p = buffer;// uninitialized if (buffer==nullptr) throw std::invalid_argument("Geometry::readPLY: failed to allocate buffer"); - is.read(buffer, vtxCount*vertexStride); + is.read(buffer, vtxCount*vtxStride); for (Vec3f &vtx : mVtx) { for (int i=0; i<3; ++i) { - float* v = (float*)(p + xyz[i].offset); - vtx[i] = (xyz[i].size == 4) ? *v : float(*(double*)(v)); - } - p += vertexStride; - } - std::free(buffer); - } -#else - if (vtx_skip[0].count == 0 && vtx_skip[1].count == 0) {//faster - if (sizeOfVertex == sizeof(float)) { - is.read((char *)(mVtx.data()), vtxCount * 3 * sizeof(float)); - } else if (sizeOfVertex == sizeof(double)) { - double *buffer = new double[vtxCount * 3], *ptr = buffer; - if (buffer==nullptr) throw std::invalid_argument("Geometry::readPLY: failed to allocate double buffer"); - is.read((char *)(buffer), vtxCount * 3 * sizeof(double)); - for (size_t i=0; i(std::malloc(vtxCount*bSize));// uninitialized - if (buffer==nullptr) throw std::invalid_argument("Geometry::readPLY: failed to allocate buffer"); - is.read(buffer, vtxCount*bSize); - for (size_t i=0; i(buffer + i*bSize + vtx_skip[0].bytes); - mVtx[i] = Vec3f(p); - } else if (sizeOfVertex == sizeof(double)) { - const double *p = reinterpret_cast(buffer + i*bSize + vtx_skip[0].bytes); - mVtx[i] = Vec3f(float(p[0]), float(p[1]), float(p[2])); - } else throw std::invalid_argument("Geometry::readPLY: expected float or double precision vertex coordinates"); + p += vtxStride; } - std::cout << "Geometry::readPLY: Warning, vertex coordinates in binary ply file were stored in double but got converted to float\n"; std::free(buffer); } -#endif - if (reverseBytes) { - auto flipBytes = [](float v)->float{ - float tmp; - char *p = (char*)&v, *q = (char*)&tmp; - q[0] = p[3]; - q[1] = p[2]; - q[2] = p[1]; - q[3] = p[0]; - return tmp; - };// flipBytes in float - for (size_t i = 0; i < mVtx.size(); ++i) { - auto &p = mVtx[i]; - p[0] = flipBytes(p[0]); - p[1] = flipBytes(p[1]); - p[2] = flipBytes(p[2]); - } - } - } else {// ascii + + } else {// ascii vertices for (auto &v : mVtx) { tokens = tokenize_line(); - if (static_cast(tokens.size()) != vtx_skip[0].count + 3 + vtx_skip[1].count) { - error("vdb_tool::readPLY: error reading ascii vertex coordinates"); - } - for (int i = 0; i<3; ++i) { - v[i] = std::stof(tokens[i + vtx_skip[0].count]); - } + if (int(tokens.size()) != vtxProps) error("vdb_tool::readPLY: error reading ascii vertex coordinates"); + for (int i = 0; i<3; ++i) v[i] = std::stof(tokens[xyz[0].id]); }// loop over vertices } // read polygon vertex lists uint32_t vtx[4]; if (format) {// binary - auto flipBytes = [&](int n){ - uint32_t tmp; - char *q = (char*)&tmp; - for (int i=0; i(std::malloc(ply_skip[0].bytes + 1));// uninitialized + char *buffer = static_cast(std::malloc(faceSkip[0].bytes + 1));// uninitialized if (buffer==nullptr) throw std::invalid_argument("Geometry::readPLY: failed to allocate buffer"); - for (size_t i=0; i unsigned int switch (n) { case 3: - is.read((char *)(&vtx), 3*sizeof(uint32_t)); - if (reverseBytes) flipBytes(3); + is.read((char*)vtx, 3*sizeof(uint32_t)); + if (reverseBytes) swapBytes(vtx, 3); mTri.emplace_back(vtx); break; case 4: - is.read((char *)(&vtx), 4*sizeof(uint32_t)); - if (reverseBytes) flipBytes(4); + is.read((char*)vtx, 4*sizeof(uint32_t)); + if (reverseBytes) swapBytes(vtx, 4); mQuad.emplace_back(vtx); break; default: throw std::invalid_argument("Geometry::readPLY: binary " + std::to_string(n) + "-gons are not supported"); break; } - is.ignore(ply_skip[1].bytes); + is.ignore(faceSkip[1].bytes); }// loop over polygons std::free(buffer); - } else {// ascii format - for (size_t i=0; i(std::stoll(tokens[i + 1 + ply_skip[0].count])); - } + const std::string polySize = tokens[faceSkip[0].count]; + const int n = std::stoi(polySize); + if (n!=3 && n!=4) throw std::invalid_argument("Geometry::readPLY: ascii " + polySize + "-gons are not supported"); + for (int i = 0, j=1+faceSkip[0].count; i(std::stoll(tokens[j])); if (n==3) { mTri.emplace_back(vtx); } else { diff --git a/openvdb_cmd/vdb_tool/include/Util.h b/openvdb_cmd/vdb_tool/include/Util.h index 96c324711c..2d7c34fa99 100644 --- a/openvdb_cmd/vdb_tool/include/Util.h +++ b/openvdb_cmd/vdb_tool/include/Util.h @@ -377,6 +377,34 @@ inline bool isLittleEndian() return (*(char *)&tmp == 1); } +/// @brief invert endianess of a type +/// @tparam T Template type to be inverted +/// @param val value to be inverted +/// @return value with reverse bytes +template +inline T swapBytes(T val) +{ + T tmp; + char *src=(char*)&val, *dst=(char*)(&tmp) + sizeof(T) - 1, *end=src + sizeof(T); + while (src != end) *dst-- = *src++; + return tmp; +} + +/// @brief invert endianess of an array of values of a specific type +/// @tparam T Template type to be inverted +/// @param val pointer to array with values to be inverted +/// @param n number of elements in the array +template +inline void swapBytes(T *val, int n) +{ + T tmp, *stop = val + n; + while(val < stop) { + char *src=(char*)val, *dst=(char*)(&tmp) + sizeof(T) - 1, *end=src + sizeof(T); + while (src != end) *dst-- = *src++; + *val++ = tmp; + } +} + /// @brief return a pseudo random uuid string. /// /// @details this function approximates a uuid version 4, variant 1 as detailed diff --git a/openvdb_cmd/vdb_tool/src/unittest.cpp b/openvdb_cmd/vdb_tool/src/unittest.cpp index 9ca0f9cbca..3d59fbd9d4 100644 --- a/openvdb_cmd/vdb_tool/src/unittest.cpp +++ b/openvdb_cmd/vdb_tool/src/unittest.cpp @@ -301,6 +301,52 @@ TEST_F(Test_vdb_tool, Util) } EXPECT_EQ(size, tmp.size()); } + + {//swapBytes + const int i = 4, j = openvdb::vdb_tool::swapBytes(i); + EXPECT_NE(i, j); + EXPECT_EQ(i, openvdb::vdb_tool::swapBytes(j)); + + const float a = 4, b = openvdb::vdb_tool::swapBytes(a); + EXPECT_NE(a, b); + EXPECT_EQ(a, openvdb::vdb_tool::swapBytes(b)); + + const double x = 4, y = openvdb::vdb_tool::swapBytes(x); + EXPECT_NE(x, y); + EXPECT_EQ(x, openvdb::vdb_tool::swapBytes(y)); + + int vec_i[3]={3,4,5}, vec_j[3]; + for (int n=0; n<3; ++n) { + vec_j[n] = openvdb::vdb_tool::swapBytes(vec_i[n]); + EXPECT_NE(vec_i[n], vec_j[n]); + } + openvdb::vdb_tool::swapBytes(vec_j, 3); + for (int n=0; n<3; ++n) EXPECT_EQ(vec_i[n], vec_j[n]); + + float vec_a[3]={3,4,5}, vec_b[3]; + for (int n=0; n<3; ++n) { + vec_b[n] = openvdb::vdb_tool::swapBytes(vec_a[n]); + EXPECT_NE(vec_a[n], vec_b[n]); + } + openvdb::vdb_tool::swapBytes(vec_b, 3); + for (int n=0; n<3; ++n) EXPECT_EQ(vec_a[n], vec_b[n]); + + double vec_x[3]={3,4,5}, vec_y[3]; + for (int n=0; n<3; ++n) { + vec_y[n] = openvdb::vdb_tool::swapBytes(vec_x[n]); + EXPECT_NE(vec_x[n], vec_y[n]); + } + openvdb::vdb_tool::swapBytes(vec_y, 3); + for (int n=0; n<3; ++n) EXPECT_EQ(vec_x[n], vec_y[n]); + } + {// weird pointer behaviour + float vec[4], *p = vec; + EXPECT_EQ(vec, p);// of course + EXPECT_EQ((char*)(vec), (char*)p);// sure + EXPECT_EQ((char*)(&vec), (char*)p);// wait, what?! + EXPECT_NE((char*)(vec), (char*)(&p));// yep + EXPECT_NE((char*)(&p), (char*)p);// of course + } }// Util TEST_F(Test_vdb_tool, getArgs)