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

- 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 @@
#include <OpenMesh/Core/IO/IOManager.hh>
#include <OpenMesh/Core/IO/BinaryHelper.hh>
#include <OpenMesh/Core/IO/writer/PLYWriter.hh>
#include <OpenMesh/Core/Utils/GenProg.hh>
#include <OpenMesh/Core/IO/SR_store.hh>
......@@ -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<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
_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<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())
_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<CustomProperty>::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<nF; ++i)
{
for (i=0, nF=int(_be.n_faces()); i<nF; ++i)
{
_be.get_vhandles(FaceHandle(i), vhandles);
_out << 3 << " ";
_out << vhandles[0].idx() << " ";
_out << vhandles[1].idx() << " ";
_out << vhandles[2].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";
}
}
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";
}
// write vertex indices per face
nV = _be.get_vhandles(FaceHandle(i), vhandles);
_out << nV;
for (size_t j=0; j<vhandles.size(); ++j)
_out << " " << vhandles[j].idx();
// write custom props
for (std::vector<CustomProperty>::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<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)
for (i=0, nV=int(_be.n_vertices()); i<nV; ++i)
......
......@@ -64,6 +64,7 @@
#include <stdio.h>
#include <string>
#include <fstream>
#include <vector>
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/SingletonT.hh>
......@@ -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<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:
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<CustomProperty>& _ovProps, std::vector<CustomProperty>& _ofProps) const;
};
......
......@@ -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