Commit a9315fdb authored by Matthias Möller's avatar Matthias Möller
Browse files

- 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
parent 59b0cafe
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#include <OpenMesh/Core/IO/IOManager.hh> #include <OpenMesh/Core/IO/IOManager.hh>
#include <OpenMesh/Core/IO/BinaryHelper.hh> #include <OpenMesh/Core/IO/BinaryHelper.hh>
#include <OpenMesh/Core/IO/writer/PLYWriter.hh> #include <OpenMesh/Core/IO/writer/PLYWriter.hh>
#include <OpenMesh/Core/Utils/GenProg.hh>
#include <OpenMesh/Core/IO/SR_store.hh> #include <OpenMesh/Core/IO/SR_store.hh>
...@@ -79,7 +80,20 @@ _PLYWriter_& PLYWriter() { return __PLYWriterInstance; } ...@@ -79,7 +80,20 @@ _PLYWriter_& PLYWriter() { return __PLYWriterInstance; }
//=== IMPLEMENTATION ========================================================== //=== 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 ...@@ -174,11 +188,115 @@ write(std::ostream& _os, BaseExporter& _be, Options _opt, std::streamsize _preci
return result; return result;
} }
//-----------------------------------------------------------------------------
// helper function for casting a property
template<typename T>
const PropertyT<T>* castProperty(const BaseProperty* _prop)
{
return dynamic_cast< const PropertyT<T>* >(_prop);
}
//-----------------------------------------------------------------------------
std::vector<_PLYWriter_::CustomProperty> _PLYWriter_::writeCustomTypeHeader(std::ostream& _out, BaseKernel::const_prop_iterator _begin, BaseKernel::const_prop_iterator _end) const
{
std::vector<CustomProperty> 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<signed char>(prop) != 0 || castProperty<char>(prop) != 0) //treat char as signed char
cProp.type = ValueTypeCHAR;
else if (castProperty<unsigned char>(prop) != 0)
cProp.type = ValueTypeUCHAR;
break;
}
case 2:
{
assert_compile (sizeof(short) == 2);
if (castProperty<signed short>(prop) != 0)
cProp.type = ValueTypeSHORT;
else if (castProperty<unsigned short>(prop) != 0)
cProp.type = ValueTypeUSHORT;
break;
}
case 4:
{
assert_compile (sizeof(int) == 4);
assert_compile (sizeof(float) == 4);
if (castProperty<signed int>(prop) != 0)
cProp.type = ValueTypeINT;
else if (castProperty<unsigned int>(prop) != 0)
cProp.type = ValueTypeUINT;
else if (castProperty<float>(prop) != 0)
cProp.type = ValueTypeFLOAT;
break;
}
case 8:
assert_compile (sizeof(double) == 8);
if (castProperty<double>(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<signed char>(_prop.property)->data()[_index];
else if (_prop.type == ValueTypeUCHAR || _prop.type == ValueTypeUINT8)
_out << " " << castProperty<unsigned char>(_prop.property)->data()[_index];
else if (_prop.type == ValueTypeSHORT)
_out << " " << castProperty<signed short>(_prop.property)->data()[_index];
else if (_prop.type == ValueTypeUSHORT)
_out << " " << castProperty<unsigned short>(_prop.property)->data()[_index];
else if (_prop.type == ValueTypeUINT)
_out << " " << castProperty<unsigned int>(_prop.property)->data()[_index];
else if (_prop.type == ValueTypeINT || _prop.type == ValueTypeINT32)
_out << " " << castProperty<signed int>(_prop.property)->data()[_index];
else if (_prop.type == ValueTypeFLOAT || _prop.type == ValueTypeFLOAT32)
_out << " " << castProperty<float>(_prop.property)->data()[_index] ;
else if (_prop.type == ValueTypeDOUBLE)
_out << " " << castProperty<double>(_prop.property)->data()[_index];
}
//-----------------------------------------------------------------------------
void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _opt, std::vector<CustomProperty>& _ovProps, std::vector<CustomProperty>& _ofProps) const {
//writing header //writing header
_out << "ply" << '\n'; _out << "ply" << '\n';
...@@ -227,8 +345,15 @@ void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _ ...@@ -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 << "element face " << _be.n_faces() << '\n';
_out << "property list uchar int vertex_indices" << '\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'; _out << "end_header" << '\n';
} }
...@@ -251,7 +376,10 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const ...@@ -251,7 +376,10 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const
VertexHandle vh; VertexHandle vh;
std::vector<VertexHandle> vhandles; std::vector<VertexHandle> vhandles;
write_header(_out, _be, _opt); std::vector<CustomProperty> vProps;
std::vector<CustomProperty> fProps;
write_header(_out, _be, _opt, vProps, fProps);
if (_opt.color_is_float()) if (_opt.color_is_float())
_out << std::fixed; _out << std::fixed;
...@@ -300,59 +428,27 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const ...@@ -300,59 +428,27 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const
} }
} }
// write custom properties for vertices
for (std::vector<CustomProperty>::iterator iter = vProps.begin(); iter < vProps.end(); ++iter)
write_customProp_ascii(_out,*iter,i);
_out << "\n"; _out << "\n";
} }
// faces (indices starting at 0) // faces (indices starting at 0)
if (_be.is_triangle_mesh()) for (i=0, nF=int(_be.n_faces()); i<nF; ++i)
{ {
for (i=0, nF=int(_be.n_faces()); i<nF; ++i) // write vertex indices per face
{ nV = _be.get_vhandles(FaceHandle(i), vhandles);
_be.get_vhandles(FaceHandle(i), vhandles); _out << nV;
_out << 3 << " "; for (size_t j=0; j<vhandles.size(); ++j)
_out << vhandles[0].idx() << " "; _out << " " << vhandles[j].idx();
_out << vhandles[1].idx() << " ";
_out << vhandles[2].idx(); // write custom props
for (std::vector<CustomProperty>::iterator iter = fProps.begin(); iter < fProps.end(); ++iter)
// //face color write_customProp_ascii(_out,*iter,i);
// if ( _opt.face_has_color() ){ _out << "\n";
// //with alpha
// if ( _opt.color_has_alpha() ){
// cA = _be.colorA( FaceHandle(i) );
// _out << " " << cA[0] << " " << cA[1] << " " << cA[2] << " " << cA[3];
// }else{
// //without alpha
// c = _be.color( FaceHandle(i) );
// _out << " " << c[0] << " " << c[1] << " " << c[2];
// }
// }
_out << "\n";
}
}
else
{
for (i=0, nF=int(_be.n_faces()); i<nF; ++i)
{
nV = _be.get_vhandles(FaceHandle(i), vhandles);
_out << nV << " ";
for (size_t j=0; j<vhandles.size(); ++j)
_out << vhandles[j].idx() << " ";
// //face color
// if ( _opt.face_has_color() ){
// //with alpha
// if ( _opt.color_has_alpha() ){
// cA = _be.colorA( FaceHandle(i) );
// _out << cA[0] << " " << cA[1] << " " << cA[2] << " " << cA[3];
// }else{
// //without alpha
// c = _be.color( FaceHandle(i) );
// _out << c[0] << " " << c[1] << " " << c[2];
// }
// }
_out << "\n";
}
} }
...@@ -435,7 +531,11 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const ...@@ -435,7 +531,11 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const
VertexHandle vh; VertexHandle vh;
std::vector<VertexHandle> vhandles; std::vector<VertexHandle> vhandles;
write_header(_out, _be, _opt); // vProps and fProps will be empty, until custom properties are supported by the binary writer
std::vector<CustomProperty> vProps;
std::vector<CustomProperty> fProps;
write_header(_out, _be, _opt, vProps, fProps);
// vertex data (point, normals, texcoords) // vertex data (point, normals, texcoords)
for (i=0, nV=int(_be.n_vertices()); i<nV; ++i) for (i=0, nV=int(_be.n_vertices()); i<nV; ++i)
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <vector>
#include <OpenMesh/Core/System/config.h> #include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/SingletonT.hh> #include <OpenMesh/Core/Utils/SingletonT.hh>
...@@ -109,15 +110,30 @@ public: ...@@ -109,15 +110,30 @@ public:
size_t binary_size(BaseExporter& _be, Options _opt) const; size_t binary_size(BaseExporter& _be, Options _opt) const;
enum ValueType { enum ValueType {
Unsupported , Unsupported = 0,
ValueTypeFLOAT32, ValueTypeFLOAT, ValueTypeFLOAT32, ValueTypeFLOAT,
ValueTypeUINT8, ValueTypeINT32, ValueTypeINT , ValueTypeINT32, ValueTypeINT , ValueTypeUINT,
ValueTypeUCHAR ValueTypeUCHAR, ValueTypeCHAR, ValueTypeUINT8,
ValueTypeUSHORT, ValueTypeSHORT,
ValueTypeDOUBLE
}; };
private: private:
mutable Options options_; 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<CustomProperty> 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: protected:
void writeValue(ValueType _type, std::ostream& _out, int value) const; void writeValue(ValueType _type, std::ostream& _out, int value) const;
void writeValue(ValueType _type, std::ostream& _out, unsigned int value) const; void writeValue(ValueType _type, std::ostream& _out, unsigned int value) const;
...@@ -125,7 +141,8 @@ protected: ...@@ -125,7 +141,8 @@ protected:
bool write_ascii(std::ostream& _out, BaseExporter&, Options) const; bool write_ascii(std::ostream& _out, BaseExporter&, Options) const;
bool write_binary(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<CustomProperty>& _ovProps, std::vector<CustomProperty>& _ofProps) const;
}; };
......
...@@ -525,4 +525,82 @@ TEST_F(OpenMeshReadWritePLY, LoadSimplePLYWithCustomProps) { ...@@ -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<unsigned short> indexProp;
OpenMesh::VPropHandleT<unsigned int> nonPersistant;
OpenMesh::VPropHandleT<double> qualityProp;
OpenMesh::FPropHandleT<signed int> 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<unsigned>(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);
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment