/* ========================================================================= * * * * OpenMesh * * Copyright (c) 2001-2015, RWTH-Aachen University * * Department of Computer Graphics and Multimedia * * All rights reserved. * * www.openmesh.org * * * *---------------------------------------------------------------------------* * This file is part of OpenMesh. * *---------------------------------------------------------------------------* * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * * * 1. Redistributions of source code must retain the above copyright notice, * * this list of conditions and the following disclaimer. * * * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * * * 3. Neither the name of the copyright holder nor the names of its * * contributors may be used to endorse or promote products derived from * * this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * * ========================================================================= */ /*===========================================================================*\ * * * $Revision$ * * $Date$ * * * \*===========================================================================*/ //== INCLUDES ================================================================= #include // -------------------- STL #if defined( OM_CC_MIPS ) #include #include #else #include #include #endif #include #include // -------------------- OpenMesh #include #include #include //=== NAMESPACES ============================================================== namespace OpenMesh { namespace IO { //=== INSTANCIATE ============================================================= // register the OMLoader singleton with MeshLoader _OMWriter_ __OMWriterInstance; _OMWriter_& OMWriter() { return __OMWriterInstance; } //=== IMPLEMENTATION ========================================================== const OMFormat::uchar _OMWriter_::magic_[3] = "OM"; const OMFormat::uint8 _OMWriter_::version_ = OMFormat::mk_version(2,0); _OMWriter_:: _OMWriter_() { IOManager().register_module(this); } bool _OMWriter_::write(const std::string& _filename, BaseExporter& _be, Options _opt, std::streamsize /*_precision*/) const { // check whether exporter can give us an OpenMesh BaseKernel if (!_be.kernel()) return false; // check for om extension in filename, we can only handle OM if (_filename.rfind(".om") == std::string::npos) return false; _opt += Options::Binary; // only binary format supported std::ofstream ofs(_filename.c_str(), std::ios::binary); // check if file is open if (!ofs.is_open()) { omerr() << "[OMWriter] : cannot open file " << _filename << std::endl; return false; } // call stream save method bool rc = write(ofs, _be, _opt); // close filestream ofs.close(); // return success/failure notice return rc; } //----------------------------------------------------------------------------- bool _OMWriter_::write(std::ostream& _os, BaseExporter& _be, Options _opt, std::streamsize /*_precision*/) const { // std::clog << "[OMWriter]::write( stream )\n"; // check exporter features if ( !check( _be, _opt ) ) { omerr() << "[OMWriter]: exporter does not support wanted feature!\n"; return false; } // Maybe an ascii version will be implemented in the future. // For now, support only a binary format if ( !_opt.check( Options::Binary ) ) _opt += Options::Binary; // Ignore LSB/MSB bit. Always store in LSB (little endian) _opt += Options::LSB; _opt -= Options::MSB; return write_binary(_os, _be, _opt); } //----------------------------------------------------------------------------- #ifndef DOXY_IGNORE_THIS template struct Enabler { explicit Enabler( T& obj ) : obj_(obj) {} ~Enabler() { obj_.enable(); } T& obj_; }; #endif bool _OMWriter_::write_binary(std::ostream& _os, BaseExporter& _be, Options _opt) const { #ifndef DOXY_IGNORE_THIS Enabler enabler(omlog()); #endif size_t bytes = 0; bool swap = _opt.check(Options::Swap) || (Endian::local() == Endian::MSB); unsigned int i, nV, nF; Vec3f v; Vec2f t; // -------------------- write header OMFormat::Header header; header.magic_[0] = 'O'; header.magic_[1] = 'M'; header.mesh_ = _be.is_triangle_mesh() ? 'T' : 'P'; header.version_ = version_; header.n_vertices_ = int(_be.n_vertices()); header.n_faces_ = int(_be.n_faces()); header.n_edges_ = int(_be.n_edges()); bytes += store( _os, header, swap ); // ---------------------------------------- write chunks OMFormat::Chunk::Header chunk_header; // -------------------- write vertex data // ---------- write vertex position if (_be.n_vertices()) { v = _be.point(VertexHandle(0)); chunk_header.reserved_ = 0; chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Vertex; chunk_header.type_ = OMFormat::Chunk::Type_Pos; chunk_header.signed_ = OMFormat::is_signed(v[0]); chunk_header.float_ = OMFormat::is_float(v[0]); chunk_header.dim_ = OMFormat::dim(v); chunk_header.bits_ = OMFormat::bits(v[0]); bytes += store( _os, chunk_header, swap ); for (i=0, nV=header.n_vertices_; i(i))); auto to_vertex_id = _be.get_to_vertex_id(HalfedgeHandle(static_cast(i))); auto face_id = _be.get_face_id(HalfedgeHandle(static_cast(i))); bytes += store( _os, next_id, OMFormat::Chunk::Integer_Size(chunk_header.bits_), swap ); bytes += store( _os, to_vertex_id, OMFormat::Chunk::Integer_Size(chunk_header.bits_), swap ); bytes += store( _os, face_id, OMFormat::Chunk::Integer_Size(chunk_header.bits_), swap ); } } // ---------- write vertex topology (outgoing halfedge) if (_be.n_vertices()) { chunk_header.reserved_ = 0; chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Vertex; chunk_header.type_ = OMFormat::Chunk::Type_Topology; chunk_header.signed_ = true; chunk_header.float_ = true; // TODO: is this correct? This causes a scalar size of 1 in OMFormat.hh scalar_size which we need I think? chunk_header.dim_ = OMFormat::Chunk::Dim_1D; chunk_header.bits_ = OMFormat::needed_bits(_be.n_edges()*4); // *2 due to halfedge ids being stored, *2 due to signedness bytes += store( _os, chunk_header, swap ); for (i=0, nV=header.n_vertices_; istore(_os, swap ); } else return false; #endif #undef NEW_STYLE } // ---------- write face color if (_be.n_faces() && _be.has_face_colors() && _opt.check( Options::FaceColor )) { #define NEW_STYLE 0 #if NEW_STYLE const BaseProperty *bp = _be.kernel()._get_fprop("f:colors"); if (bp) { #endif Vec3uc c; chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Face; chunk_header.type_ = OMFormat::Chunk::Type_Color; chunk_header.signed_ = OMFormat::is_signed( c[0] ); chunk_header.float_ = OMFormat::is_float( c[0] ); chunk_header.dim_ = OMFormat::dim( c ); chunk_header.bits_ = OMFormat::bits( c[0] ); bytes += store( _os, chunk_header, swap ); #if !NEW_STYLE for (i=0, nF=header.n_faces_; istore(_os, swap); } else return false; #endif } // ---------- write vertex status if (_be.n_vertices() && _be.has_vertex_status() && _opt.check(Options::Status)) { auto s = _be.status(VertexHandle(0)); chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Vertex; chunk_header.type_ = OMFormat::Chunk::Type_Status; chunk_header.signed_ = false; chunk_header.float_ = false; chunk_header.dim_ = OMFormat::Chunk::Dim_1D; chunk_header.bits_ = OMFormat::bits(s); // std::clog << chunk_header << std::endl; bytes += store(_os, chunk_header, swap); for (i = 0, nV = header.n_vertices_; i < nV; ++i) bytes += store(_os, _be.status(VertexHandle(i)), swap); } // ---------- write edge status if (_be.n_edges() && _be.has_edge_status() && _opt.check(Options::Status)) { auto s = _be.status(EdgeHandle(0)); chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Edge; chunk_header.type_ = OMFormat::Chunk::Type_Status; chunk_header.signed_ = false; chunk_header.float_ = false; chunk_header.dim_ = OMFormat::Chunk::Dim_1D; chunk_header.bits_ = OMFormat::bits(s); // std::clog << chunk_header << std::endl; bytes += store(_os, chunk_header, swap); for (i = 0, nV = header.n_edges_; i < nV; ++i) bytes += store(_os, _be.status(EdgeHandle(i)), swap); } // ---------- write halfedge status if (_be.n_edges() && _be.has_halfedge_status() && _opt.check(Options::Status)) { auto s = _be.status(HalfedgeHandle(0)); chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Halfedge; chunk_header.type_ = OMFormat::Chunk::Type_Status; chunk_header.signed_ = false; chunk_header.float_ = false; chunk_header.dim_ = OMFormat::Chunk::Dim_1D; chunk_header.bits_ = OMFormat::bits(s); // std::clog << chunk_header << std::endl; bytes += store(_os, chunk_header, swap); for (i = 0, nV = header.n_edges_ * 2; i < nV; ++i) bytes += store(_os, _be.status(HalfedgeHandle(i)), swap); } // ---------- write face status if (_be.n_faces() && _be.has_face_status() && _opt.check(Options::Status)) { auto s = _be.status(FaceHandle(0)); chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Face; chunk_header.type_ = OMFormat::Chunk::Type_Status; chunk_header.signed_ = false; chunk_header.float_ = false; chunk_header.dim_ = OMFormat::Chunk::Dim_1D; chunk_header.bits_ = OMFormat::bits(s); // std::clog << chunk_header << std::endl; bytes += store(_os, chunk_header, swap); for (i = 0, nV = header.n_faces_; i < nV; ++i) bytes += store(_os, _be.status(FaceHandle(i)), swap); } // -------------------- write custom properties BaseKernel::const_prop_iterator prop; for (prop = _be.kernel()->vprops_begin(); prop != _be.kernel()->vprops_end(); ++prop) { if ( !*prop ) continue; if ( (*prop)->name()[1]==':') continue; bytes += store_binary_custom_chunk(_os, **prop, OMFormat::Chunk::Entity_Vertex, swap ); } for (prop = _be.kernel()->fprops_begin(); prop != _be.kernel()->fprops_end(); ++prop) { if ( !*prop ) continue; if ( (*prop)->name()[1]==':') continue; bytes += store_binary_custom_chunk(_os, **prop, OMFormat::Chunk::Entity_Face, swap ); } for (prop = _be.kernel()->eprops_begin(); prop != _be.kernel()->eprops_end(); ++prop) { if ( !*prop ) continue; if ( (*prop)->name()[1]==':') continue; bytes += store_binary_custom_chunk(_os, **prop, OMFormat::Chunk::Entity_Edge, swap ); } for (prop = _be.kernel()->hprops_begin(); prop != _be.kernel()->hprops_end(); ++prop) { if ( !*prop ) continue; if ( (*prop)->name()[1]==':') continue; bytes += store_binary_custom_chunk(_os, **prop, OMFormat::Chunk::Entity_Halfedge, swap ); } for (prop = _be.kernel()->mprops_begin(); prop != _be.kernel()->mprops_end(); ++prop) { if ( !*prop ) continue; if ( (*prop)->name()[1]==':') continue; bytes += store_binary_custom_chunk(_os, **prop, OMFormat::Chunk::Entity_Mesh, swap ); } memset(&chunk_header, 0, sizeof(chunk_header)); chunk_header.name_ = false; chunk_header.entity_ = OMFormat::Chunk::Entity_Sentinel; bytes += store(_os, chunk_header, swap); std::clog << "#bytes written: " << bytes << std::endl; return true; } // ---------------------------------------------------------------------------- size_t _OMWriter_::store_binary_custom_chunk(std::ostream& _os, const BaseProperty& _bp, OMFormat::Chunk::Entity _entity, bool _swap) const { //omlog() << "Custom Property " << OMFormat::as_string(_entity) << " property [" // << _bp.name() << "]" << std::endl; // Don't store if // 1. it is not persistent // 2. it's name is empty if ( !_bp.persistent() || _bp.name().empty() ) { //omlog() << " skipped\n"; return 0; } size_t bytes = 0; OMFormat::Chunk::esize_t element_size = OMFormat::Chunk::esize_t(_bp.element_size()); OMFormat::Chunk::Header chdr; // set header chdr.name_ = true; chdr.entity_ = _entity; chdr.type_ = OMFormat::Chunk::Type_Custom; chdr.signed_ = 0; chdr.float_ = 0; chdr.dim_ = OMFormat::Chunk::Dim_1D; // ignored chdr.bits_ = element_size; // write custom chunk // 1. chunk header bytes += store( _os, chdr, _swap ); // 2. property name bytes += store( _os, OMFormat::Chunk::PropertyName(_bp.name()), _swap ); // 3. block size bytes += store( _os, _bp.size_of(), OMFormat::Chunk::Integer_32, _swap ); //omlog() << " n_bytes = " << _bp.size_of() << std::endl; // 4. data { size_t b; bytes += ( b=_bp.store( _os, _swap ) ); //omlog() << " b = " << b << std::endl; assert( b == _bp.size_of() ); } return bytes; } // ---------------------------------------------------------------------------- size_t _OMWriter_::binary_size(BaseExporter& /* _be */, Options /* _opt */) const { // std::clog << "[OMWriter]: binary_size()" << std::endl; size_t bytes = sizeof( OMFormat::Header ); // !!!TODO!!! return bytes; } //============================================================================= } // namespace IO } // namespace OpenMesh //=============================================================================