diff --git a/include/mitsuba/render/trimesh.h b/include/mitsuba/render/trimesh.h index e5cebcc2..d6200c0e 100644 --- a/include/mitsuba/render/trimesh.h +++ b/include/mitsuba/render/trimesh.h @@ -304,9 +304,12 @@ public: static ref fromBlender(const std::string &name, size_t faceCount, void *facePtr, size_t vertexCount, void *vertexPtr, void *uvPtr, void *colPtr, short matNr); - /// Export an Wavefront OBJ version of this file + /// Export a Wavefront OBJ version of this file void writeOBJ(const fs::path &path) const; + /// Export a Stanford PLY version of this file + void writePLY(const fs::path &path) const; + /// Return a string representation std::string toString() const; diff --git a/src/libpython/render.cpp b/src/libpython/render.cpp index 7d498aba..3bc25afd 100644 --- a/src/libpython/render.cpp +++ b/src/libpython/render.cpp @@ -565,6 +565,7 @@ void export_render() { .def("serialize", triMesh_serialize1) .def("serialize", triMesh_serialize2) .def("writeOBJ", &TriMesh::writeOBJ) + .def("writePLY", &TriMesh::writePLY) .def("fromBlender", trimesh_fromBlender) .staticmethod("fromBlender"); diff --git a/src/librender/trimesh.cpp b/src/librender/trimesh.cpp index 6adacbf9..c3ee4aef 100644 --- a/src/librender/trimesh.cpp +++ b/src/librender/trimesh.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -84,7 +85,6 @@ TriMesh::TriMesh(Stream *stream, int index) loadCompressed(stream, index); } - /* Flags used to identify available data during serialization */ enum ETriMeshFlags { EHasNormals = 0x0001, @@ -1057,6 +1057,81 @@ void TriMesh::writeOBJ(const fs::path &path) const { os.close(); } +void TriMesh::writePLY(const fs::path &path) const { + fs::ofstream os(path, std::ios::out | std::ios::binary); + + os << "ply\n"; + if (Stream::getHostByteOrder() == Stream::ELittleEndian) + os << "format binary_little_endian 1.0\n"; + else + os << "format binary_big_endian 1.0\n"; + os << "comment generated by Mitsuba " << MTS_VERSION << "\n"; + os << "element vertex " << getVertexCount() << "\n"; + + size_t storagePerVertex = 3 * sizeof(float); + os << "property float x\n"; + os << "property float y\n"; + os << "property float z\n"; + + if (m_normals) { + os << "property float nx\n"; + os << "property float ny\n"; + os << "property float nz\n"; + storagePerVertex += 3 * sizeof(float); + } + + if (m_texcoords) { + os << "property float u\n"; + os << "property float v\n"; + storagePerVertex += 2 * sizeof(float); + } + + if (m_colors) { + os << "property uchar red\n"; + os << "property uchar green\n"; + os << "property uchar blue\n"; + storagePerVertex += 3 * sizeof(uint8_t); + } + + os << "element face " << m_triangleCount << "\n"; + os << "property list uchar int vertex_indices\n"; + os << "end_header\n"; + + size_t vertexStorageSize = storagePerVertex * (size_t) m_vertexCount; + uint8_t *vertexStorage = new uint8_t[vertexStorageSize], *ptr = vertexStorage; + + for (size_t i=0; i< getVertexCount(); ++i) { + Vector3f p(m_positions[i]); memcpy(ptr, &p, sizeof(Vector3f)); ptr += sizeof(Vector3f); + if (m_normals) { + Vector3f n(m_normals[i]); memcpy(ptr, &n, sizeof(Vector3f)); ptr += sizeof(Vector3f); + } + if (m_texcoords) { + Vector2f uv(m_texcoords[i]); memcpy(ptr, &uv, sizeof(Vector2f)); ptr += sizeof(Vector2f); + } + if (m_colors) { + *ptr += (uint8_t) std::max(0.0f, std::min(255.0f, m_colors[i][0] * 255.0f + 0.5f)); + *ptr += (uint8_t) std::max(0.0f, std::min(255.0f, m_colors[i][1] * 255.0f + 0.5f)); + *ptr += (uint8_t) std::max(0.0f, std::min(255.0f, m_colors[i][2] * 255.0f + 0.5f)); + } + } + Assert(ptr-vertexStorage == vertexStorageSize); + os.write((const char *) vertexStorage, vertexStorageSize); + delete[] vertexStorage; + + size_t faceStorageSize = (sizeof(Triangle) + 1) * (size_t) getTriangleCount(); + uint8_t *faceStorage = new uint8_t[faceStorageSize]; + ptr = faceStorage; + for (size_t i=0; i stream = _stream; diff --git a/src/shapes/ply.cpp b/src/shapes/ply.cpp index 3cc16358..1c2f38fd 100644 --- a/src/shapes/ply.cpp +++ b/src/shapes/ply.cpp @@ -273,7 +273,8 @@ public: void face_vertex_indices_begin_uint8(ply::uint8 size) { if (size != 3 && size != 4) - Log(EError, "Only triangle and quad-based PLY meshes are supported for now."); + Log(EError, "Encountered a face with %i vertices! " + "Only triangle and quad-based PLY meshes are supported for now.", size); m_indexCtr = 0; }