- 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/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; }
|
|||||||
//=== 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
|
|||||||
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& _
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
|||||||
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,60 +428,28 @@ 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)
|
|
||||||
{
|
|
||||||
_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)
|
for (i=0, nF=int(_be.n_faces()); i<nF; ++i)
|
||||||
{
|
{
|
||||||
|
// write vertex indices per face
|
||||||
nV = _be.get_vhandles(FaceHandle(i), vhandles);
|
nV = _be.get_vhandles(FaceHandle(i), vhandles);
|
||||||
_out << nV << " ";
|
_out << nV;
|
||||||
for (size_t j=0; j<vhandles.size(); ++j)
|
for (size_t j=0; j<vhandles.size(); ++j)
|
||||||
_out << vhandles[j].idx() << " ";
|
_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];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
// write custom props
|
||||||
|
for (std::vector<CustomProperty>::iterator iter = fProps.begin(); iter < fProps.end(); ++iter)
|
||||||
|
write_customProp_ascii(_out,*iter,i);
|
||||||
_out << "\n";
|
_out << "\n";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -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 @@
|
|||||||
#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:
|
|||||||
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:
|
|||||||
|
|
||||||
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) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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