- 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
This commit is contained in:
@@ -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();
|
||||
// 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();
|
||||
|
||||
// //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 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user