From a9315fdb589c745ffc9ddc38da85d6fb4820093d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20M=C3=B6ller?= Date: Tue, 16 Jun 2015 08:52:20 +0000 Subject: [PATCH] - ply writer: add custom property support for ascii version refs #2496 closes #2480 git-svn-id: http://www.openmesh.org/svnrepo/OpenMesh/trunk@1285 fdac6126-5c0c-442c-9429-916003d36597 --- src/OpenMesh/Core/IO/writer/PLYWriter.cc | 202 ++++++++++++++++------ src/OpenMesh/Core/IO/writer/PLYWriter.hh | 25 ++- src/Unittests/unittests_read_write_PLY.cc | 78 +++++++++ 3 files changed, 250 insertions(+), 55 deletions(-) diff --git a/src/OpenMesh/Core/IO/writer/PLYWriter.cc b/src/OpenMesh/Core/IO/writer/PLYWriter.cc index 599ffcc1..6108b0c6 100644 --- a/src/OpenMesh/Core/IO/writer/PLYWriter.cc +++ b/src/OpenMesh/Core/IO/writer/PLYWriter.cc @@ -56,6 +56,7 @@ #include #include #include +#include #include @@ -79,7 +80,20 @@ _PLYWriter_& PLYWriter() { return __PLYWriterInstance; } //=== IMPLEMENTATION ========================================================== -_PLYWriter_::_PLYWriter_() { IOManager().register_module(this); } +_PLYWriter_::_PLYWriter_() +{ + IOManager().register_module(this); + + nameOfType_[Unsupported] = ""; + nameOfType_[ValueTypeCHAR] = "char"; + nameOfType_[ValueTypeUCHAR] = nameOfType_[ValueTypeUINT8] = "uchar"; + nameOfType_[ValueTypeUSHORT] = "ushort"; + nameOfType_[ValueTypeSHORT] = "short"; + nameOfType_[ValueTypeUINT] = "uint"; + nameOfType_[ValueTypeINT] = "int"; + nameOfType_[ValueTypeFLOAT32] = nameOfType_[ValueTypeFLOAT] = "float"; + nameOfType_[ValueTypeDOUBLE] = "double"; +} //----------------------------------------------------------------------------- @@ -174,11 +188,115 @@ write(std::ostream& _os, BaseExporter& _be, Options _opt, std::streamsize _preci return result; } +//----------------------------------------------------------------------------- + +// helper function for casting a property +template +const PropertyT* castProperty(const BaseProperty* _prop) +{ + return dynamic_cast< const PropertyT* >(_prop); +} + +//----------------------------------------------------------------------------- +std::vector<_PLYWriter_::CustomProperty> _PLYWriter_::writeCustomTypeHeader(std::ostream& _out, BaseKernel::const_prop_iterator _begin, BaseKernel::const_prop_iterator _end) const +{ + std::vector customProps; + for (;_begin != _end; ++_begin) + { + BaseProperty* prop = *_begin; + + + // check, if property is persistant + if (!prop->persistent()) + continue; + + + // identify type of property + CustomProperty cProp(prop); + size_t propSize = prop->element_size(); + switch (propSize) + { + case 1: + { + assert_compile(sizeof(char) == 1); + //check, if prop is a char or unsigned char by dynamic_cast + //char, unsigned char and signed char are 3 distinct types + if (castProperty(prop) != 0 || castProperty(prop) != 0) //treat char as signed char + cProp.type = ValueTypeCHAR; + else if (castProperty(prop) != 0) + cProp.type = ValueTypeUCHAR; + break; + } + case 2: + { + assert_compile (sizeof(short) == 2); + if (castProperty(prop) != 0) + cProp.type = ValueTypeSHORT; + else if (castProperty(prop) != 0) + cProp.type = ValueTypeUSHORT; + break; + } + case 4: + { + assert_compile (sizeof(int) == 4); + assert_compile (sizeof(float) == 4); + if (castProperty(prop) != 0) + cProp.type = ValueTypeINT; + else if (castProperty(prop) != 0) + cProp.type = ValueTypeUINT; + else if (castProperty(prop) != 0) + cProp.type = ValueTypeFLOAT; + break; + + } + case 8: + assert_compile (sizeof(double) == 8); + if (castProperty(prop) != 0) + cProp.type = ValueTypeDOUBLE; + break; + default: + break; + } + + if (cProp.type != Unsupported) + { + // property type was identified and it is persistant, write into header + customProps.push_back(cProp); + _out << "property " << nameOfType_[cProp.type] << " " << cProp.property->name() << "\n"; + } + } + return customProps; +} //----------------------------------------------------------------------------- -void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _opt) const { +void _PLYWriter_::write_customProp_ascii(std::ostream& _out, const CustomProperty& _prop, size_t _index) const +{ + if (_prop.type == ValueTypeCHAR) + _out << " " << castProperty(_prop.property)->data()[_index]; + else if (_prop.type == ValueTypeUCHAR || _prop.type == ValueTypeUINT8) + _out << " " << castProperty(_prop.property)->data()[_index]; + else if (_prop.type == ValueTypeSHORT) + _out << " " << castProperty(_prop.property)->data()[_index]; + else if (_prop.type == ValueTypeUSHORT) + _out << " " << castProperty(_prop.property)->data()[_index]; + else if (_prop.type == ValueTypeUINT) + _out << " " << castProperty(_prop.property)->data()[_index]; + else if (_prop.type == ValueTypeINT || _prop.type == ValueTypeINT32) + _out << " " << castProperty(_prop.property)->data()[_index]; + else if (_prop.type == ValueTypeFLOAT || _prop.type == ValueTypeFLOAT32) + _out << " " << castProperty(_prop.property)->data()[_index] ; + else if (_prop.type == ValueTypeDOUBLE) + _out << " " << castProperty(_prop.property)->data()[_index]; +} + + +//----------------------------------------------------------------------------- + + + +void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _opt, std::vector& _ovProps, std::vector& _ofProps) const { //writing header _out << "ply" << '\n'; @@ -227,8 +345,15 @@ void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _ } } + if (!_opt.is_binary()) // binary not supported yet + _ovProps = writeCustomTypeHeader(_out, _be.kernel()->vprops_begin(), _be.kernel()->vprops_end()); + _out << "element face " << _be.n_faces() << '\n'; _out << "property list uchar int vertex_indices" << '\n'; + + if (!_opt.is_binary()) // binary not supported yet + _ofProps = writeCustomTypeHeader(_out, _be.kernel()->fprops_begin(), _be.kernel()->fprops_end()); + _out << "end_header" << '\n'; } @@ -251,7 +376,10 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const VertexHandle vh; std::vector vhandles; - write_header(_out, _be, _opt); + std::vector vProps; + std::vector fProps; + + write_header(_out, _be, _opt, vProps, fProps); if (_opt.color_is_float()) _out << std::fixed; @@ -300,59 +428,27 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const } } + + // write custom properties for vertices + for (std::vector::iterator iter = vProps.begin(); iter < vProps.end(); ++iter) + write_customProp_ascii(_out,*iter,i); + _out << "\n"; } // faces (indices starting at 0) - if (_be.is_triangle_mesh()) + for (i=0, nF=int(_be.n_faces()); i::iterator iter = fProps.begin(); iter < fProps.end(); ++iter) + write_customProp_ascii(_out,*iter,i); + _out << "\n"; } @@ -435,7 +531,11 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const VertexHandle vh; std::vector vhandles; - write_header(_out, _be, _opt); + // vProps and fProps will be empty, until custom properties are supported by the binary writer + std::vector vProps; + std::vector fProps; + + write_header(_out, _be, _opt, vProps, fProps); // vertex data (point, normals, texcoords) for (i=0, nV=int(_be.n_vertices()); i #include #include +#include #include #include @@ -109,15 +110,30 @@ public: size_t binary_size(BaseExporter& _be, Options _opt) const; enum ValueType { - Unsupported , + Unsupported = 0, ValueTypeFLOAT32, ValueTypeFLOAT, - ValueTypeUINT8, ValueTypeINT32, ValueTypeINT , - ValueTypeUCHAR + ValueTypeINT32, ValueTypeINT , ValueTypeUINT, + ValueTypeUCHAR, ValueTypeCHAR, ValueTypeUINT8, + ValueTypeUSHORT, ValueTypeSHORT, + ValueTypeDOUBLE }; private: mutable Options options_; + struct CustomProperty + { + ValueType type; + const BaseProperty* property; + CustomProperty(const BaseProperty* const _p):type(Unsupported),property(_p){} + }; + + const char* nameOfType_[12]; + + /// write custom persistant properties into the header for the current element, returns all properties, which were written sorted + std::vector writeCustomTypeHeader(std::ostream& _out, BaseKernel::const_prop_iterator _begin, BaseKernel::const_prop_iterator _end) const; + void write_customProp_ascii(std::ostream& _our, const CustomProperty& _prop, size_t _index) const; + protected: void writeValue(ValueType _type, std::ostream& _out, int value) const; void writeValue(ValueType _type, std::ostream& _out, unsigned int value) const; @@ -125,7 +141,8 @@ protected: bool write_ascii(std::ostream& _out, BaseExporter&, Options) const; bool write_binary(std::ostream& _out, BaseExporter&, Options) const; - void write_header(std::ostream& _out, BaseExporter& _be, Options& _opt) const; + /// write header into the stream _out. Returns custom properties (vertex and face) which are written into the header + void write_header(std::ostream& _out, BaseExporter& _be, Options& _opt, std::vector& _ovProps, std::vector& _ofProps) const; }; diff --git a/src/Unittests/unittests_read_write_PLY.cc b/src/Unittests/unittests_read_write_PLY.cc index a0a3115d..9cffffa8 100644 --- a/src/Unittests/unittests_read_write_PLY.cc +++ b/src/Unittests/unittests_read_write_PLY.cc @@ -525,4 +525,82 @@ TEST_F(OpenMeshReadWritePLY, LoadSimplePLYWithCustomProps) { } + +TEST_F(OpenMeshReadWritePLY, WriteReadSimplePLYWithCustomProps) { + + PolyMesh mesh; + + OpenMesh::IO::Options options; + bool ok = OpenMesh::IO::read_mesh(mesh, "cube-minimal.ply", options); + + + OpenMesh::VPropHandleT indexProp; + OpenMesh::VPropHandleT nonPersistant; + OpenMesh::VPropHandleT qualityProp; + OpenMesh::FPropHandleT faceProp; + + const std::string indexPropName = "mySuperIndexProperty"; + const std::string qualityPropName = "quality"; + const std::string facePropName = "anotherPropForFaces"; + const std::string nonPersistantName = "nonPersistant"; + + mesh.add_property(indexProp,indexPropName); + mesh.add_property(qualityProp,qualityPropName); + mesh.add_property(faceProp,facePropName); + mesh.add_property(nonPersistant,nonPersistantName); + + mesh.property(indexProp).set_persistent(true); + mesh.property(qualityProp).set_persistent(true); + mesh.property(faceProp).set_persistent(true); + + signed char i=0; + for (Mesh::VertexIter v_iter = mesh.vertices_begin(); v_iter != mesh.vertices_end(); ++v_iter, ++i) + { + mesh.property(indexProp, *v_iter) = i; + mesh.property(qualityProp, *v_iter) = 3.5*i; + } + + i = 0; + for (Mesh::FaceIter f_iter = mesh.faces_begin(); f_iter != mesh.faces_end(); ++f_iter, ++i) + { + mesh.property(faceProp, *f_iter) = -i; + } + + const char* outFilename = "cube-minimal-customprops_openmeshOutputTestfile.ply"; + ok = OpenMesh::IO::write_mesh(mesh, outFilename); + + ASSERT_TRUE(ok); + + PolyMesh loadedMesh; + + EXPECT_FALSE(loadedMesh.get_property_handle(indexProp,indexPropName)) << "Could access to property which was deleted"; + + options += OpenMesh::IO::Options::Custom; + ok = OpenMesh::IO::read_mesh(loadedMesh, outFilename, options); + + ASSERT_TRUE(ok); + + + ASSERT_TRUE(loadedMesh.get_property_handle(indexProp,indexPropName)) << "Could not access index property"; + ASSERT_TRUE(loadedMesh.get_property_handle(qualityProp,qualityPropName)) << "Could not access quality property"; + ASSERT_TRUE(loadedMesh.get_property_handle(faceProp,facePropName)) << "Could not access face property"; + EXPECT_FALSE(loadedMesh.get_property_handle(nonPersistant,nonPersistantName)) << "Could access non persistant property"; + + i=0; + for (Mesh::VertexIter v_iter = loadedMesh.vertices_begin(); v_iter != loadedMesh.vertices_end(); ++v_iter, ++i) + { + EXPECT_EQ(loadedMesh.property(indexProp, *v_iter), static_cast(i)); + EXPECT_EQ(loadedMesh.property(qualityProp, *v_iter),3.5*i); + } + + i = 0; + for (Mesh::FaceIter f_iter = loadedMesh.faces_begin(); f_iter != loadedMesh.faces_end(); ++f_iter, ++i) + { + EXPECT_EQ(loadedMesh.property(faceProp, *f_iter),-i); + } + + + remove(outFilename); + +} }