From 32e2932f933f463dd8e58bf5e14630eb76e863f3 Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Thu, 27 Jul 2017 18:19:40 -0700 Subject: [PATCH 1/2] import code from https://github.com/maruncz/tinyply --- CMakeLists.txt | 8 + src/common/tinyply/tinyply.cpp | 459 ++++++++++++++++++++++++++++++ src/common/tinyply/tinyply.h | 497 +++++++++++++++++++++++++++++++++ src/main_ply.cc | 219 +++++++++++++++ 4 files changed, 1183 insertions(+) create mode 100644 src/common/tinyply/tinyply.cpp create mode 100644 src/common/tinyply/tinyply.h create mode 100644 src/main_ply.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ec076c..b342ee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,6 +119,14 @@ target_link_libraries(voidstar string(REPLACE ";" " " glfw_static_ldflags "${GLFW_STATIC_LDFLAGS}") set_property(TARGET voidstar APPEND_STRING PROPERTY LINK_FLAGS "${glfw_static_ldflags}") +set(PLY_SOURCES + src/main_ply.cc + # src/Algorithm.cc + src/Algo3DCubeContiFrebet.cc + src/common/tinyply/tinyply.cpp + ) +add_executable(ply ${PLY_SOURCES}) + #----------------------------------------------------------------------------- # PACKAGING #----------------------------------------------------------------------------- diff --git a/src/common/tinyply/tinyply.cpp b/src/common/tinyply/tinyply.cpp new file mode 100644 index 0000000..cb1c9c9 --- /dev/null +++ b/src/common/tinyply/tinyply.cpp @@ -0,0 +1,459 @@ +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com) +// https://github.com/ddiakopoulos/tinyply + +#include "tinyply.h" + +#include +#include + +using namespace tinyply; +using namespace std; + +////////////////// +// PLY Property // +////////////////// + +PlyProperty::PlyProperty(std::istream & is) : isList(false) +{ + parse_internal(is); +} + +void PlyProperty::parse_internal(std::istream & is) +{ + string type; + is >> type; + if (type == "list") + { + string countType; + is >> countType >> type; + listType = property_type_from_string(countType); + isList = true; + listCount = 0; + } + propertyType = property_type_from_string(type); + is >> name; +} + +///////////////// +// PLY Element // +///////////////// + +PlyElement::PlyElement(std::istream & is) +{ + parse_internal(is); +} + +void PlyElement::parse_internal(std::istream & is) +{ + is >> name >> size; +} + +////////////// +// PLY File // +////////////// + +PlyFile::PlyFile(std::istream & is) +{ + if (!parse_header(is)) + { + throw std::runtime_error("file is not ply or encounted junk in header"); + } +} + +bool PlyFile::parse_header(std::istream & is) +{ + std::string line; + while (std::getline(is, line)) + { + std::istringstream ls(line); + std::string token; + ls >> token; + if (token == "ply" || token == "PLY" || token == "") + { + continue; + } + else if (token == "comment") read_header_text(line, comments, 8); + else if (token == "format") read_header_format(ls); + else if (token == "element") read_header_element(ls); + else if (token == "property") read_header_property(ls); + else if (token == "obj_info") read_header_text(line, objInfo, 9); + else if (token == "end_header") break; + else return false; + } + return true; +} + +void PlyFile::read_header_text(std::string line, std::vector& place, int erase) +{ + place.push_back((erase > 0) ? line.erase(0, erase) : line); +} + +void PlyFile::read_header_format(std::istream & is) +{ + std::string s; + (is >> s); + if (s == "binary_little_endian") isBinary = true; + else if (s == "binary_big_endian") isBinary = isBigEndian = true; +} + +void PlyFile::read_header_element(std::istream & is) +{ + get_elements().emplace_back(is); +} + +void PlyFile::read_header_property(std::istream & is) +{ + get_elements().back().properties.emplace_back(is); +} + +size_t PlyFile::skip_property_binary(const PlyProperty & property, std::istream & is) +{ + static std::vector skip(PropertyTable[property.propertyType].stride); + if (property.isList) + { + size_t listSize = 0; + size_t dummyCount = 0; + read_property_binary(property.listType, &listSize, dummyCount, is); + for (size_t i = 0; i < listSize; ++i) is.read(skip.data(), PropertyTable[property.propertyType].stride); + return listSize; + } + else + { + is.read(skip.data(), PropertyTable[property.propertyType].stride); + return 0; + } +} + +void PlyFile::skip_property_ascii(const PlyProperty & property, std::istream & is) +{ + std::string skip; + if (property.isList) + { + int listSize; + is >> listSize; + for (int i = 0; i < listSize; ++i) is >> skip; + } + else is >> skip; +} + +void PlyFile::read_property_binary(PlyProperty::Type t, void * dest, size_t & destOffset, std::istream & is) +{ + static std::vector src(PropertyTable[t].stride); + is.read(src.data(), PropertyTable[t].stride); + + switch (t) + { + case PlyProperty::Type::INT8: ply_cast(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::UINT8: ply_cast(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::INT16: ply_cast(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::UINT16: ply_cast(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::INT32: ply_cast(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::UINT32: ply_cast(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::FLOAT32: ply_cast_float(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::FLOAT64: ply_cast_double(dest, src.data(), isBigEndian); break; + case PlyProperty::Type::INVALID: throw std::invalid_argument("invalid ply property"); + } + destOffset += PropertyTable[t].stride; +} + +void PlyFile::read_property_ascii(PlyProperty::Type t, void * dest, size_t & destOffset, std::istream & is) +{ + switch (t) + { + case PlyProperty::Type::INT8: *((int8_t *)dest) = ply_read_ascii(is); break; + case PlyProperty::Type::UINT8: *((uint8_t *)dest) = ply_read_ascii(is); break; + case PlyProperty::Type::INT16: ply_cast_ascii(dest, is); break; + case PlyProperty::Type::UINT16: ply_cast_ascii(dest, is); break; + case PlyProperty::Type::INT32: ply_cast_ascii(dest, is); break; + case PlyProperty::Type::UINT32: ply_cast_ascii(dest, is); break; + case PlyProperty::Type::FLOAT32: ply_cast_ascii(dest, is); break; + case PlyProperty::Type::FLOAT64: ply_cast_ascii(dest, is); break; + case PlyProperty::Type::INVALID: throw std::invalid_argument("invalid ply property"); + } + destOffset += PropertyTable[t].stride; +} + +void PlyFile::write_property_ascii(PlyProperty::Type t, std::ostream & os, uint8_t * src, size_t & srcOffset) +{ + switch (t) + { + case PlyProperty::Type::INT8: os << static_cast(*reinterpret_cast(src)); break; + case PlyProperty::Type::UINT8: os << static_cast(*reinterpret_cast(src)); break; + case PlyProperty::Type::INT16: os << *reinterpret_cast(src); break; + case PlyProperty::Type::UINT16: os << *reinterpret_cast(src); break; + case PlyProperty::Type::INT32: os << *reinterpret_cast(src); break; + case PlyProperty::Type::UINT32: os << *reinterpret_cast(src); break; + case PlyProperty::Type::FLOAT32: os << *reinterpret_cast(src); break; + case PlyProperty::Type::FLOAT64: os << *reinterpret_cast(src); break; + case PlyProperty::Type::INVALID: throw std::invalid_argument("invalid ply property"); + } + srcOffset += PropertyTable[t].stride; +} + +void PlyFile::write_property_binary(PlyProperty::Type t, std::ostream & os, uint8_t * src, size_t & srcOffset) +{ + os.write((char *)src, PropertyTable[t].stride); + srcOffset += PropertyTable[t].stride; +} + +void PlyFile::read(std::istream & is) +{ + read_internal(is); +} + +void PlyFile::write(std::ostream & os, bool isBinary) +{ + if (isBinary) write_binary_internal(os); + else write_ascii_internal(os); +} + +void PlyFile::write_binary_internal(std::ostream & os) +{ + isBinary = true; + write_header(os); + + for (auto & e : elements) + { + for (size_t i = 0; i < e.size; ++i) + { + for (auto & p : e.properties) + { + auto & cursor = userDataTable[make_key(e.name, p.name)]; + assert(cursor->data); + if (p.isList) + { + // fixed-length list + if (p.listCount >= 1) + { + uint8_t listSize[4] = {0, 0, 0, 0}; + memcpy(listSize, &p.listCount, sizeof(uint32_t)); + size_t dummyCount = 0; + write_property_binary(p.listType, os, listSize, dummyCount); + for (int j = 0; j < p.listCount; ++j) + { + write_property_binary(p.propertyType, os, (cursor->data + cursor->offset), cursor->offset); + } + } + else // variable-length list + { + size_t offset = 0; + void* src_vec = (void*)(&cursor->data[cursor->offset]); + uint8_t* src_data = nullptr; + get_data(p.propertyType, src_vec, src_data); + size_t src_size = 0; + get_size(p.propertyType, src_vec, src_size); + + uint8_t listSize[4] = {0, 0, 0, 0}; + memcpy(listSize, &src_size, sizeof(uint32_t)); + size_t dummyCount = 0; + write_property_binary(p.listType, os, listSize, dummyCount); + if (src_size > 0) assert(src_data); + for (size_t j = 0; j < src_size; ++j) + { + write_property_binary(p.propertyType, os, (src_data + offset), offset); + } + + // Vector pointer offset + cursor->offset += VectorPropertyTable[p.propertyType].stride; + } + } + else + { + write_property_binary(p.propertyType, os, (cursor->data + cursor->offset), cursor->offset); + } + } + } + } +} + +void PlyFile::write_ascii_internal(std::ostream & os) +{ + write_header(os); + + for (auto & e : elements) + { + for (size_t i = 0; i < e.size; ++i) + { + size_t idx = 0; + for (auto & p : e.properties) + { + auto & cursor = userDataTable[make_key(e.name, p.name)]; + assert(cursor); + assert(cursor->data); + if (idx++ > 0) os << " "; + if (p.isList) + { + // fixed-length list + if (p.listCount >= 1) + { + os << p.listCount; + for (int j = 0; j < p.listCount; ++j) + { + os << " "; + write_property_ascii(p.propertyType, os, (cursor->data + cursor->offset), cursor->offset); + } + } + else // variable-length list + { + size_t offset = 0; + void* src_vec = (void*)(&cursor->data[cursor->offset]); + uint8_t* src_data = nullptr; + get_data(p.propertyType, src_vec, src_data); + size_t src_size = 0; + get_size(p.propertyType, src_vec, src_size); + + os << src_size; + for (size_t j = 0; j < src_size; ++j) + { + os << " "; + write_property_ascii(p.propertyType, os, (src_data + offset), offset); + } + + // Vector pointer offset + cursor->offset += VectorPropertyTable[p.propertyType].stride; + } + } + else + { + write_property_ascii(p.propertyType, os, (cursor->data + cursor->offset), cursor->offset); + } + } + os << std::endl; + } + } +} + +void PlyFile::write_header(std::ostream & os) +{ + const std::locale & fixLoc = std::locale("C"); + os.imbue(fixLoc); + + os << "ply" << std::endl; + if (isBinary) + os << ((isBigEndian) ? "format binary_big_endian 1.0" : "format binary_little_endian 1.0") << std::endl; + else + os << "format ascii 1.0" << std::endl; + + for (const auto & comment : comments) + os << "comment " << comment << std::endl; + + for (auto & e : elements) + { + os << "element " << e.name << " " << e.size << std::endl; + for (const auto & p : e.properties) + { + if (p.isList) + { + os << "property list " << PropertyTable[p.listType].str << " " + << PropertyTable[p.propertyType].str << " " << p.name << std::endl; + } + else + { + os << "property " << PropertyTable[p.propertyType].str << " " << p.name << std::endl; + } + } + } + os << "end_header" << std::endl; +} + +void PlyFile::read_internal(std::istream & is) +{ + std::function read; + std::function skip; + if (isBinary) + { + read = [&](PlyProperty::Type t, void * dest, size_t & destOffset, std::istream & is) { read_property_binary(t, dest, destOffset, is); }; + skip = [&](const PlyProperty & property, std::istream & is) { skip_property_binary(property, is); }; + } + else + { + read = [&](PlyProperty::Type t, void * dest, size_t & destOffset, std::istream & is) { read_property_ascii(t, dest, destOffset, is); }; + skip = [&](const PlyProperty & property, std::istream & is) { skip_property_ascii(property, is); }; + } + + auto skip_element = [&](const PlyElement & element, std::istream & is) { + for (size_t count = 0; count < element.size; ++count) + { + for (auto& property : element.properties) + { + skip(property, is); + } + } + }; + + std::set> processed_cursors; + for (auto & element : get_elements()) + { + if (std::find(requestedElements.begin(), requestedElements.end(), element.name) != requestedElements.end()) + { + for (size_t count = 0; count < element.size; ++count) + { + for (auto & property : element.properties) + { + if (auto & cursor = userDataTable[make_key(element.name, property.name)]) + { + assert(cursor->data); + if (property.isList) + { + size_t listSize = 0; + size_t dummyCount = 0; + read(property.listType, &listSize, dummyCount, is); + if (property.listCount >= 1) + { + if (listSize != property.listCount) + throw std::runtime_error("fixed-length list expected"); + if (cursor->realloc == false) + { + cursor->realloc = true; + resize_vector(property.propertyType, cursor->vector, listSize * element.size, cursor->data); + } + for (size_t i = 0; i < listSize; ++i) + { + read(property.propertyType, (cursor->data + cursor->offset), cursor->offset, is); + } + } + else // variable-length lists + { + size_t offset = 0; + void* dst_vec = (void*)(&cursor->data[cursor->offset]); + uint8_t* dst_data = nullptr; + + // Resize inner vector + resize_vector(property.propertyType, dst_vec, listSize, dst_data); + if (listSize > 0) assert(dst_data); + for (size_t i = 0; i < listSize; ++i) + { + read(property.propertyType, (dst_data + offset), offset, is); + } + // Vector pointer offset + cursor->offset += VectorPropertyTable[property.propertyType].stride; + } + } + else + { + read(property.propertyType, (cursor->data + cursor->offset), cursor->offset, is); + } + processed_cursors.insert(cursor); + } + else + { + skip(property, is); + } + } + } + } + else + { + skip_element(element, is); + } + } + + // Reset offsets + for (auto& c: processed_cursors) + { + c->offset = 0; + } +} diff --git a/src/common/tinyply/tinyply.h b/src/common/tinyply/tinyply.h new file mode 100644 index 0000000..050d9e4 --- /dev/null +++ b/src/common/tinyply/tinyply.h @@ -0,0 +1,497 @@ +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com) +// https://github.com/ddiakopoulos/tinyply +// Forked in https://github.com/maruncz/tinyply + +#ifndef tinyply_h +#define tinyply_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace tinyply +{ + + template T endian_swap(const T & v) { return v; } + template<> inline uint16_t endian_swap(const uint16_t & v) { return (v << 8) | (v >> 8); } + template<> inline uint32_t endian_swap(const uint32_t & v) { return (v << 24) | ((v << 8) & 0x00ff0000) | ((v >> 8) & 0x0000ff00) | (v >> 24); } + template<> inline uint64_t endian_swap(const uint64_t & v) + { + return (((v & 0x00000000000000ffLL) << 56) | + ((v & 0x000000000000ff00LL) << 40) | + ((v & 0x0000000000ff0000LL) << 24) | + ((v & 0x00000000ff000000LL) << 8) | + ((v & 0x000000ff00000000LL) >> 8) | + ((v & 0x0000ff0000000000LL) >> 24) | + ((v & 0x00ff000000000000LL) >> 40) | + ((v & 0xff00000000000000LL) >> 56)); + } + template<> inline int16_t endian_swap(const int16_t & v) { uint16_t r = endian_swap(*(uint16_t*)&v); return *(int16_t*)&r; } + template<> inline int32_t endian_swap(const int32_t & v) { uint32_t r = endian_swap(*(uint32_t*)&v); return *(int32_t*)&r; } + template<> inline int64_t endian_swap(const int64_t & v) { uint64_t r = endian_swap(*(uint64_t*)&v); return *(int64_t*)&r; } + inline float endian_swap_float(const uint32_t & v) { uint32_t r = endian_swap(v); return *(float*)&r; } + inline double endian_swap_double(const uint64_t & v) { uint64_t r = endian_swap(v); return *(double*)&r; } + + struct DataCursor + { + void * vector; + uint8_t * data; + size_t offset; + bool realloc = false; + }; + + class PlyProperty + { + void parse_internal(std::istream & is); + public: + + enum class Type : uint8_t + { + INVALID, + INT8, + UINT8, + INT16, + UINT16, + INT32, + UINT32, + FLOAT32, + FLOAT64 + }; + + PlyProperty(std::istream & is); + PlyProperty(Type type, const std::string & name) : propertyType(type), isList(false), name(name) {} + PlyProperty(Type list_type, Type prop_type, const std::string & name, int listCount) : listType(list_type), propertyType(prop_type), isList(true), listCount(listCount), name(name) {} + + Type listType, propertyType; + bool isList; + int listCount = 0; + std::string name; + }; + + inline std::string make_key(const std::string & a, const std::string & b) + { + return (a + "-" + b); + } + + template + void ply_cast(void * dest, const char * src, bool be) + { + *(static_cast(dest)) = (be) ? endian_swap(*(reinterpret_cast(src))) : *(reinterpret_cast(src)); + } + + template + void ply_cast_float(void * dest, const char * src, bool be) + { + *(static_cast(dest)) = (be) ? endian_swap_float(*(reinterpret_cast(src))) : *(reinterpret_cast(src)); + } + + template + void ply_cast_double(void * dest, const char * src, bool be) + { + *(static_cast(dest)) = (be) ? endian_swap_double(*(reinterpret_cast(src))) : *(reinterpret_cast(src)); + } + + template + T ply_read_ascii(std::istream & is) + { + T data; + is >> data; + return data; + } + + template + void ply_cast_ascii(void * dest, std::istream & is) + { + *(static_cast(dest)) = ply_read_ascii(is); + } + + struct PropertyInfo { int stride; std::string str; }; + static std::map PropertyTable + { + { PlyProperty::Type::INT8,{ 1, "char" } }, + { PlyProperty::Type::UINT8,{ 1, "uchar" } }, + { PlyProperty::Type::INT16,{ 2, "short" } }, + { PlyProperty::Type::UINT16,{ 2, "ushort" } }, + { PlyProperty::Type::INT32,{ 4, "int" } }, + { PlyProperty::Type::UINT32,{ 4, "uint" } }, + { PlyProperty::Type::FLOAT32,{ 4, "float" } }, + { PlyProperty::Type::FLOAT64,{ 8, "double" } }, + { PlyProperty::Type::INVALID,{ 0, "INVALID" } } + }; + + static std::map VectorPropertyTable + { + { PlyProperty::Type::INT8,{ sizeof(std::vector), "char" } }, + { PlyProperty::Type::UINT8,{ sizeof(std::vector), "uchar" } }, + { PlyProperty::Type::INT16,{ sizeof(std::vector), "short" } }, + { PlyProperty::Type::UINT16,{ sizeof(std::vector), "ushort" } }, + { PlyProperty::Type::INT32,{ sizeof(std::vector), "int" } }, + { PlyProperty::Type::UINT32,{ sizeof(std::vector), "uint" } }, + { PlyProperty::Type::FLOAT32,{ sizeof(std::vector), "float" } }, + { PlyProperty::Type::FLOAT64,{ sizeof(std::vector), "double" } }, + { PlyProperty::Type::INVALID,{ 0, "INVALID" } } + }; + + inline PlyProperty::Type property_type_from_string(const std::string & t) + { + if (t == "int8" || t == "char") return PlyProperty::Type::INT8; + else if (t == "uint8" || t == "uchar") return PlyProperty::Type::UINT8; + else if (t == "int16" || t == "short") return PlyProperty::Type::INT16; + else if (t == "uint16" || t == "ushort") return PlyProperty::Type::UINT16; + else if (t == "int32" || t == "int") return PlyProperty::Type::INT32; + else if (t == "uint32" || t == "uint") return PlyProperty::Type::UINT32; + else if (t == "float32" || t == "float") return PlyProperty::Type::FLOAT32; + else if (t == "float64" || t == "double") return PlyProperty::Type::FLOAT64; + return PlyProperty::Type::INVALID; + } + + template + inline uint8_t * resize(void * v, size_t newSize) + { + auto vec = static_cast *>(v); + vec->resize(newSize); + return reinterpret_cast(vec->data()); + } + + inline void resize_vector(const PlyProperty::Type t, void * v, size_t newSize, uint8_t *& ptr) + { + switch (t) + { + case PlyProperty::Type::INT8: ptr = resize(v, newSize); break; + case PlyProperty::Type::UINT8: ptr = resize(v, newSize); break; + case PlyProperty::Type::INT16: ptr = resize(v, newSize); break; + case PlyProperty::Type::UINT16: ptr = resize(v, newSize); break; + case PlyProperty::Type::INT32: ptr = resize(v, newSize); break; + case PlyProperty::Type::UINT32: ptr = resize(v, newSize); break; + case PlyProperty::Type::FLOAT32: ptr = resize(v, newSize); break; + case PlyProperty::Type::FLOAT64: ptr = resize(v, newSize); break; + case PlyProperty::Type::INVALID: throw std::invalid_argument("invalid ply property"); + } + } + + template + inline uint8_t * get_data(void * v) + { + auto vec = static_cast *>(v); + return reinterpret_cast(vec->data()); + } + + inline void get_data(const PlyProperty::Type t, void * v, uint8_t *& ptr) + { + switch (t) + { + case PlyProperty::Type::INT8: ptr = get_data(v); break; + case PlyProperty::Type::UINT8: ptr = get_data(v); break; + case PlyProperty::Type::INT16: ptr = get_data(v); break; + case PlyProperty::Type::UINT16: ptr = get_data(v); break; + case PlyProperty::Type::INT32: ptr = get_data(v); break; + case PlyProperty::Type::UINT32: ptr = get_data(v); break; + case PlyProperty::Type::FLOAT32: ptr = get_data(v); break; + case PlyProperty::Type::FLOAT64: ptr = get_data(v); break; + case PlyProperty::Type::INVALID: throw std::invalid_argument("invalid ply property"); + } + } + + template + inline size_t get_size(void * v) + { + auto vec = static_cast *>(v); + return vec->size(); + } + + inline void get_size(const PlyProperty::Type t, void * v, size_t& s) + { + switch (t) + { + case PlyProperty::Type::INT8: s = get_size(v); break; + case PlyProperty::Type::UINT8: s = get_size(v); break; + case PlyProperty::Type::INT16: s = get_size(v); break; + case PlyProperty::Type::UINT16: s = get_size(v); break; + case PlyProperty::Type::INT32: s = get_size(v); break; + case PlyProperty::Type::UINT32: s = get_size(v); break; + case PlyProperty::Type::FLOAT32: s = get_size(v); break; + case PlyProperty::Type::FLOAT64: s = get_size(v); break; + case PlyProperty::Type::INVALID: throw std::invalid_argument("invalid ply property"); + } + } + + template + inline PlyProperty::Type property_type_for_type(typename std::enable_if::value>::type* = 0) + { + if (std::is_same::value) return PlyProperty::Type::INT8; + else if (std::is_same::value) return PlyProperty::Type::UINT8; + else if (std::is_same::value) return PlyProperty::Type::INT16; + else if (std::is_same::value) return PlyProperty::Type::UINT16; + else if (std::is_same::value) return PlyProperty::Type::INT32; + else if (std::is_same::value) return PlyProperty::Type::UINT32; + else if (std::is_same::value) return PlyProperty::Type::FLOAT32; + else if (std::is_same::value) return PlyProperty::Type::FLOAT64; + else return PlyProperty::Type::INVALID; + } + + template + inline PlyProperty::Type property_type_for_type(typename std::enable_if::value>::type* = 0) + { + return property_type_for_type(); + } + + template + inline bool is_vector_type(typename std::enable_if::value>::type* = 0) + { + return true; + } + + template + inline bool is_vector_type(typename std::enable_if::value>::type* = 0) + { + return false; + } + + class PlyElement + { + void parse_internal(std::istream & is); + public: + PlyElement(std::istream & istream); + PlyElement(const std::string & name, size_t count) : name(name), size(count) {} + std::string name; + size_t size; + std::vector properties; + }; + + inline int find_element(const std::string key, std::vector & list) + { + for (size_t i = 0; i < list.size(); ++i) + { + if (list[i].name == key) + { + return i; + } + } + return -1; + } + + class PlyFile + { + + public: + + PlyFile() {} + PlyFile(std::istream & is); + + void read(std::istream & is); + void write(std::ostream & os, bool isBinary); + + inline bool is_binary() const + { + return isBinary; + } + + std::vector & get_elements() { return elements; } + + std::vector comments; + std::vector objInfo; + + template + size_t request_properties_from_element(const std::string & elementKey, std::vector propertyKeys, std::vector & source, int listCount = 1) + { + if (get_elements().size() == 0) + return 0; + + if (find_element(elementKey, get_elements()) >= 0) + { + if (std::find(requestedElements.begin(), requestedElements.end(), elementKey) == requestedElements.end()) + requestedElements.push_back(elementKey); + } + else return 0; + + // If a vector> is provided, override listCount + if (is_vector_type()) + listCount = 0; + + // count and verify large enough + auto instance_counter = [&](const std::string & elementKey, const std::string & propertyKey) + { + for (auto e : get_elements()) + { + if (e.name != elementKey) continue; + for (auto p : e.properties) + { + if (p.name == propertyKey) + { + if (PropertyTable[property_type_for_type()].stride != PropertyTable[p.propertyType].stride) + throw std::runtime_error("destination vector is wrongly typed to hold this property"); + return e.size; + + } + } + } + return size_t(0); + }; + + // Check if requested key is in the parsed header + std::vector unusedKeys; + for (auto key : propertyKeys) + { + for (auto e : get_elements()) + { + if (e.name != elementKey) continue; + std::vector headerKeys; + for (auto p : e.properties) + { + headerKeys.push_back(p.name); + } + + if (std::find(headerKeys.begin(), headerKeys.end(), key) == headerKeys.end()) + { + unusedKeys.push_back(key); + } + + } + } + + // Not using them? Don't let them affect the propertyKeys count used for calculating array sizes + for (auto k : unusedKeys) + { + propertyKeys.erase(std::remove(propertyKeys.begin(), propertyKeys.end(), k), propertyKeys.end()); + } + if (!propertyKeys.size()) return 0; + + // All requested properties in the userDataTable share the same cursor (thrown into the same flat array) + auto cursor = std::make_shared(); + + std::vector instanceCounts; + + for (auto key : propertyKeys) + { + if (int instanceCount = instance_counter(elementKey, key)) + { + instanceCounts.push_back(instanceCount); + auto result = userDataTable.insert(std::pair>(make_key(elementKey, key), cursor)); + if (result.second == false) + throw std::invalid_argument("property has already been requested: " + key); + } + else continue; + } + + size_t totalInstanceSize = [&]() { size_t t = 0; for (auto c : instanceCounts) { t += c; } return t; }(); + if (listCount >= 1) + totalInstanceSize *= listCount; + source.resize(totalInstanceSize); // this satisfies regular properties; `cursor->realloc` is for list types since tinyply uses single-pass parsing + cursor->offset = 0; + cursor->vector = &source; + cursor->data = reinterpret_cast(source.data()); + + // Update list count for fixed-length lists + if (propertyKeys.size() == 1 && listCount > 1) + { + for (auto& e : get_elements()) + { + if (e.name != elementKey) continue; + for (auto& p : e.properties) + { + if (p.name == propertyKeys[0]) + { + // Update list count of property + p.listCount = listCount; + } + } + } + }; + + if (listCount > 1) + { + cursor->realloc = true; + return (totalInstanceSize / propertyKeys.size()) / listCount; + } + + return totalInstanceSize / propertyKeys.size(); + } + + template + void add_properties_to_element(const std::string & elementKey, const std::vector & propertyKeys, std::vector & source, const int listCount = 1, const PlyProperty::Type listType = PlyProperty::Type::INVALID) + { + auto cursor = std::make_shared(); + cursor->offset = 0; + cursor->vector = &source; + cursor->data = reinterpret_cast(source.data()); + + auto create_property_on_element = [&](PlyElement & e) + { + for (auto key : propertyKeys) + { + PlyProperty::Type t = property_type_for_type(); + PlyProperty newProp = (listType == PlyProperty::Type::INVALID) ? PlyProperty(t, key) : PlyProperty(listType, t, key, listCount); + userDataTable.insert(std::pair>(make_key(e.name, key), cursor)); + e.properties.push_back(newProp); + } + }; + + int idx = find_element(elementKey, elements); + if (idx >= 0) + { + PlyElement & e = elements[idx]; + create_property_on_element(e); + } + else + { + PlyElement newElement = + (listCount == 1) // single value + ? PlyElement( + elementKey, + source.size() / propertyKeys.size()) + : ((listCount > 1) // fixed-length list + ? PlyElement(elementKey, + source.size() / listCount) + : PlyElement(elementKey, source.size())); // variable-length list + create_property_on_element(newElement); + elements.push_back(newElement); + } + } + + private: + + size_t skip_property_binary(const PlyProperty & property, std::istream & is); + void skip_property_ascii(const PlyProperty & property, std::istream & is); + + void read_property_binary(PlyProperty::Type t, void * dest, size_t & destOffset, std::istream & is); + void read_property_ascii(PlyProperty::Type t, void * dest, size_t & destOffset, std::istream & is); + void write_property_ascii(PlyProperty::Type t, std::ostream & os, uint8_t * src, size_t & srcOffset); + void write_property_binary(PlyProperty::Type t, std::ostream & os, uint8_t * src, size_t & srcOffset); + + bool parse_header(std::istream & is); + void write_header(std::ostream & os); + + void read_header_format(std::istream & is); + void read_header_element(std::istream & is); + void read_header_property(std::istream & is); + void read_header_text(std::string line, std::vector & place, int erase = 0); + + void read_internal(std::istream & is); + + void write_ascii_internal(std::ostream & os); + void write_binary_internal(std::ostream & os); + + bool isBinary = false; + bool isBigEndian = false; + + std::map> userDataTable; + + std::vector elements; + std::vector requestedElements; + }; + +} // namesapce tinyply + +#endif // tinyply_h diff --git a/src/main_ply.cc b/src/main_ply.cc new file mode 100644 index 0000000..02480be --- /dev/null +++ b/src/main_ply.cc @@ -0,0 +1,219 @@ +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. +// Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com) +// https://github.com/ddiakopoulos/tinyply + +#include +#include +#include +#include +#include +#include + +#include "tinyply/tinyply.h" + +using namespace tinyply; + +typedef std::chrono::time_point timepoint; +std::chrono::high_resolution_clock c; + +inline std::chrono::time_point now() +{ + return c.now(); +} + +inline double difference_micros(timepoint start, timepoint end) +{ + return (double)std::chrono::duration_cast(end - start).count(); +} + +void write_ply_example(const std::string & filename) +{ + std::vector verts; + std::vector norms; + std::vector colors; + + std::vector vertexIndicies; + std::vector faceTexcoords; + + // Per-vertex elements + verts = { + 0.f, -100.462f, -142.5f, + 123.409f, -100.462f, 71.25f, + 0.f, 100.462f, 0.f, + -123.409f, -100.462f, 71.25f, + 0.f, -100.462f, -142.5f, + 0.f, -100.462f, -142.5f, + 123.409f, -100.462f, 71.25f, + 123.409f, -100.462f, 71.25f, + 0.f, 100.462f, 0.f, + 0.f, 100.462f, 0.f, + -123.409f, -100.462f, 71.25f, + -123.409f, -100.462f, 71.25f + }; + + norms = { + 0.853811f, 0.349609f, -0.492948f, + 0.853811f, 0.349609f, -0.492948f, + 0.0f, 0.350761f, 0.989145f, + 0.0f, 0.349609f, 0.985896f, + -0.853811f, 0.349609f, -0.492948f, + 0.0f, -1.0472f, 0.0f, + 0.0f, 0.349609f, 0.985896f, + 0.0f, -1.0472f, 0.0f, + 0.856624f, 0.350761f, -0.494572f, + -0.856624f, 0.350761f, -0.494572f, + -0.853811f, 0.349609f, -0.492948f, + 0.0f, -1.0472f, 0.0f + }; + + colors = { + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 192, 192, 255, + 192, 193, 194, 255, + 195, 196, 197, 255 + }; + + // Per-face elements + vertexIndicies = { 6, 2, 3, 0, 8, 1, 10, 9, 4, 5, 7, 11 }; + faceTexcoords = { + 0.199362f, 0.679351f, 0.399522f, + 0.333583f, 0.599682f, 0.679351f, + 0.000000f, 0.332206f, 0.399522f, + 0.333583f, 0.199362f, 0.679351f, + 0.599682f, 0.679351f, 0.399522f, + 0.333583f, 0.799044f, 0.332206f, + 0.799044f, 0.332206f, 1.000000f, + 0.678432f, 0.599682f, 0.679351f + }; + + // Tinyply does not perform any file i/o internally + std::filebuf fb; + fb.open(filename, std::ios::out | std::ios::binary); + std::ostream outputStream(&fb); + + PlyFile myFile; + + myFile.add_properties_to_element("vertex", { "x", "y", "z" }, verts); + myFile.add_properties_to_element("vertex", { "nx", "ny", "nz" }, norms); + myFile.add_properties_to_element("vertex", { "red", "green", "blue", "alpha" }, colors); + + // List property types must also be created with a count and type of the list (data property type + // is automatically inferred from the type of the vector argument). + myFile.add_properties_to_element("face", { "vertex_indices" }, vertexIndicies, 3, PlyProperty::Type::UINT8); + myFile.add_properties_to_element("face", { "texcoord" }, faceTexcoords, 6, PlyProperty::Type::UINT8); + + myFile.comments.push_back("generated by tinyply"); + myFile.write(outputStream, true); + + fb.close(); +} + +void read_ply_file(const std::string & filename) +{ + // Tinyply can and will throw exceptions at you! + try + { + // Read the file and create a std::istringstream suitable + // for the lib -- tinyply does not perform any file i/o. + std::ifstream ss(filename, std::ios::binary); + + // Parse the ASCII header fields + PlyFile file(ss); + + for (auto e : file.get_elements()) + { + std::cout << "element - " << e.name << " (" << e.size << ")" << std::endl; + for (auto p : e.properties) + { + std::cout << "\tproperty - " << p.name << " (" << PropertyTable[p.propertyType].str << ")" << std::endl; + } + } + std::cout << std::endl; + + for (auto c : file.comments) + { + std::cout << "Comment: " << c << std::endl; + } + + // Define containers to hold the extracted data. The type must match + // the property type given in the header. Tinyply will interally allocate the + // the appropriate amount of memory. + std::vector verts; + std::vector norms; + std::vector colors; + + std::vector> faces; + std::vector uvCoords; + + // The count returns the number of instances of the property group. The vectors + // above will be resized into a multiple of the property group size as + // they are "flattened"... i.e. verts = {x, y, z, x, y, z, ...} + uint32_t vertexCount = file.request_properties_from_element("vertex", { "x", "y", "z" }, verts); + uint32_t normalCount = file.request_properties_from_element("vertex", { "nx", "ny", "nz" }, norms); + uint32_t colorCount = file.request_properties_from_element("vertex", { "red", "green", "blue", "alpha" }, colors); + + // For properties that are list types, it is possibly to specify the expected count (ideal if a + // consumer of this library knows the layout of their format a-priori). Otherwise, tinyply + // defers allocation of memory until the first instance of the property has been found + // as implemented in file.read(ss) + uint32_t faceCount = file.request_properties_from_element("face", { "vertex_indices" }, faces, 3); + uint32_t faceTexcoordCount = file.request_properties_from_element("face", { "texcoord" }, uvCoords, 6); + + // Now populate the vectors... + timepoint before = now(); + file.read(ss); + timepoint after = now(); + + // Good place to put a breakpoint! + std::cout << "Parsing took " << difference_micros(before, after) << "μs: " << std::endl; + std::cout << "\tRead " << verts.size() << " total vertices (" << vertexCount << " properties)." << std::endl; + std::cout << "\tRead " << norms.size() << " total normals (" << normalCount << " properties)." << std::endl; + std::cout << "\tRead " << colors.size() << " total vertex colors (" << colorCount << " properties)." << std::endl; + std::cout << "\tRead " << faces.size() << " total faces (triangles) (" << faceCount << " properties)." << std::endl; + std::cout << "\tRead " << uvCoords.size() << " total texcoords (" << faceTexcoordCount << " properties)." << std::endl; + + /* + // Fixme - tinyply isn't particularly sensitive to mismatched properties and prefers to crash instead of throw. Use + // actual data from parsed headers instead of manually defining properties added to a new file like below: + + std::filebuf fb; + fb.open("converted.ply", std::ios::out | std::ios::binary); + std::ostream outputStream(&fb); + + PlyFile myFile; + + myFile.add_properties_to_element("vertex", { "x", "y", "z" }, verts); + myFile.add_properties_to_element("vertex", { "red", "green", "blue" }, colors); + myFile.add_properties_to_element("face", { "vertex_indices" }, faces, 3, PlyProperty::Type::UINT8); + + myFile.comments.push_back("generated by tinyply"); + myFile.write(outputStream, true); + + fb.close(); + */ + } + + catch (const std::exception & e) + { + std::cerr << "Caught exception: " << e.what() << std::endl; + } +} + +int main(int argc, char *argv[]) +{ + std::cout << argc << std::endl << argv << std::endl; + + write_ply_example("example_tetrahedron.ply"); + read_ply_file("example_tetrahedron.ply"); + return 0; +} From b4276c633d79d183b96e93c591bcb0496d8202bd Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Thu, 27 Jul 2017 18:20:20 -0700 Subject: [PATCH 2/2] ply: fixes from PR github.com/fenollp/tinyply/pull/1 --- src/common/tinyply/tinyply.cpp | 4 ++-- src/common/tinyply/tinyply.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/tinyply/tinyply.cpp b/src/common/tinyply/tinyply.cpp index cb1c9c9..aa6fae2 100644 --- a/src/common/tinyply/tinyply.cpp +++ b/src/common/tinyply/tinyply.cpp @@ -232,7 +232,7 @@ void PlyFile::write_binary_internal(std::ostream & os) memcpy(listSize, &p.listCount, sizeof(uint32_t)); size_t dummyCount = 0; write_property_binary(p.listType, os, listSize, dummyCount); - for (int j = 0; j < p.listCount; ++j) + for (size_t j = 0; j < p.listCount; ++j) { write_property_binary(p.propertyType, os, (cursor->data + cursor->offset), cursor->offset); } @@ -290,7 +290,7 @@ void PlyFile::write_ascii_internal(std::ostream & os) if (p.listCount >= 1) { os << p.listCount; - for (int j = 0; j < p.listCount; ++j) + for (size_t j = 0; j < p.listCount; ++j) { os << " "; write_property_ascii(p.propertyType, os, (cursor->data + cursor->offset), cursor->offset); diff --git a/src/common/tinyply/tinyply.h b/src/common/tinyply/tinyply.h index 050d9e4..5634413 100644 --- a/src/common/tinyply/tinyply.h +++ b/src/common/tinyply/tinyply.h @@ -72,11 +72,11 @@ namespace tinyply PlyProperty(std::istream & is); PlyProperty(Type type, const std::string & name) : propertyType(type), isList(false), name(name) {} - PlyProperty(Type list_type, Type prop_type, const std::string & name, int listCount) : listType(list_type), propertyType(prop_type), isList(true), listCount(listCount), name(name) {} + PlyProperty(Type list_type, Type prop_type, const std::string & name, size_t listCount) : listType(list_type), propertyType(prop_type), isList(true), listCount(listCount), name(name) {} Type listType, propertyType; bool isList; - int listCount = 0; + size_t listCount = 0; std::string name; };