From 3fbc166201f5371542f55e7da1f424b19788b18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Thu, 21 Feb 2019 12:49:56 +0100 Subject: [PATCH 1/2] - PLY Reader/Writer: Support for face colors (Thanks to Steve and Barb Demlow for the patch) --- Doc/changelog.docu | 1 + src/OpenMesh/Core/IO/exporter/ExporterT.hh | 4 +- src/OpenMesh/Core/IO/reader/PLYReader.cc | 134 ++++++++++++++--- src/OpenMesh/Core/IO/writer/PLYWriter.cc | 80 ++++++++-- src/OpenMesh/Core/IO/writer/PLYWriter.hh | 1 + .../TestFiles/cube-minimal-faceColors.ply | 33 +++++ src/Unittests/unittests_read_write_PLY.cc | 139 ++++++++++++++++++ 7 files changed, 362 insertions(+), 30 deletions(-) create mode 100644 src/Unittests/TestFiles/cube-minimal-faceColors.ply diff --git a/Doc/changelog.docu b/Doc/changelog.docu index 5ff01c21..6908061d 100644 --- a/Doc/changelog.docu +++ b/Doc/changelog.docu @@ -29,6 +29,7 @@
  • PLY Reader: Fixed endless loop on unknown property list type
  • PLY Reader: Fix hang when reading directly from istream (Thanks to Paul LorĂ© for the patch)
  • PLY Reader: Fix file load for ASCII PLY without a newline at the end of the file (Thanks to Mathieu Lamarre for the patch ) +
  • PLY Reader/Writer: Support for face colors (Thanks to Steve and Barb Demlow for the patch)
  • OM Writer/Reader: Update file format version to 2.0. Older files can still be read, but older OpenMesh versions cannot read new format.
  • OM Writer/Reader: Fixed inconsistent writing/reading of edge properties
  • OM Writer/Reader: Add option to store status
  • diff --git a/src/OpenMesh/Core/IO/exporter/ExporterT.hh b/src/OpenMesh/Core/IO/exporter/ExporterT.hh index cffe07e2..b788988c 100644 --- a/src/OpenMesh/Core/IO/exporter/ExporterT.hh +++ b/src/OpenMesh/Core/IO/exporter/ExporterT.hh @@ -335,14 +335,14 @@ public: Vec3f colorf(FaceHandle _fh) const override { - return (mesh_.has_vertex_colors() + return (mesh_.has_face_colors() ? color_cast(mesh_.color(_fh)) : Vec3f(0, 0, 0)); } Vec4f colorAf(FaceHandle _fh) const override { - return (mesh_.has_vertex_colors() + return (mesh_.has_face_colors() ? color_cast(mesh_.color(_fh)) : Vec4f(0, 0, 0, 0)); } diff --git a/src/OpenMesh/Core/IO/reader/PLYReader.cc b/src/OpenMesh/Core/IO/reader/PLYReader.cc index 4617afe9..2c49ff17 100644 --- a/src/OpenMesh/Core/IO/reader/PLYReader.cc +++ b/src/OpenMesh/Core/IO/reader/PLYReader.cc @@ -425,6 +425,12 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options // faces for (i = 0; i < faceCount_ && !_in.eof(); ++i) { FaceHandle fh; + + c[0] = 0; + c[1] = 0; + c[2] = 0; + c[3] = 255; + for (size_t propertyIndex = 0; propertyIndex < e_it->properties_.size(); ++propertyIndex) { PropertyInfo prop = e_it->properties_[propertyIndex]; switch (prop.property) { @@ -456,6 +462,38 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options ++complex_faces; break; + case COLORRED: + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { + _in >> tmp; + c[0] = static_cast (tmp * 255.0f); + } else + _in >> c[0]; + break; + + case COLORGREEN: + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { + _in >> tmp; + c[1] = static_cast (tmp * 255.0f); + } else + _in >> c[1]; + break; + + case COLORBLUE: + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { + _in >> tmp; + c[2] = static_cast (tmp * 255.0f); + } else + _in >> c[2]; + break; + + case COLORALPHA: + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { + _in >> tmp; + c[3] = static_cast (tmp * 255.0f); + } else + _in >> c[3]; + break; + case CUSTOM_PROP: if (_opt.check(Options::Custom) && fh.is_valid()) readCustomProperty(_in, _bi, fh, prop.name, prop.value, prop.listIndexType); @@ -468,7 +506,8 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options break; } } - + if (_opt.face_has_color()) + _bi.set_color(fh, Vec4uc(c)); } } else @@ -568,29 +607,26 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap readValue(prop.value, _in, t[1]); break; case COLORRED: - if (prop.value == ValueTypeFLOAT32 || - prop.value == ValueTypeFLOAT) { - readValue(prop.value, _in, tmp); + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { + readValue(prop.value, _in, tmp); - c[0] = static_cast (tmp * 255.0f); + c[0] = static_cast (tmp * 255.0f); } else - readInteger(prop.value, _in, c[0]); + readInteger(prop.value, _in, c[0]); break; case COLORGREEN: - if (prop.value == ValueTypeFLOAT32 || - prop.value == ValueTypeFLOAT) { - readValue(prop.value, _in, tmp); - c[1] = static_cast (tmp * 255.0f); - } - else - readInteger(prop.value, _in, c[1]); + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { + readValue(prop.value, _in, tmp); + c[1] = static_cast (tmp * 255.0f); + } + else + readInteger(prop.value, _in, c[1]); break; case COLORBLUE: - if (prop.value == ValueTypeFLOAT32 || - prop.value == ValueTypeFLOAT) { + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { readValue(prop.value, _in, tmp); c[2] = static_cast (tmp * 255.0f); } @@ -599,8 +635,7 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap break; case COLORALPHA: - if (prop.value == ValueTypeFLOAT32 || - prop.value == ValueTypeFLOAT) { + if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) { readValue(prop.value, _in, tmp); c[3] = static_cast (tmp * 255.0f); } @@ -634,6 +669,12 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap else if (e_it->element_ == FACE) { for (unsigned i = 0; i < e_it->count_ && !_in.eof(); ++i) { FaceHandle fh; + + c[0] = 0; + c[1] = 0; + c[2] = 0; + c[3] = 255; + for (size_t propertyIndex = 0; propertyIndex < e_it->properties_.size(); ++propertyIndex) { PropertyInfo prop = e_it->properties_[propertyIndex]; @@ -668,7 +709,38 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap if (!fh.is_valid()) ++complex_faces; break; - + case COLORRED: + if (prop.value == ValueTypeFLOAT32 || + prop.value == ValueTypeFLOAT) { + readValue(prop.value, _in, tmp); + c[0] = static_cast (tmp * 255.0f); + } else + readInteger(prop.value, _in, c[0]); + break; + case COLORGREEN: + if (prop.value == ValueTypeFLOAT32 || + prop.value == ValueTypeFLOAT) { + readValue(prop.value, _in, tmp); + c[1] = static_cast (tmp * 255.0f); + } else + readInteger(prop.value, _in, c[1]); + break; + case COLORBLUE: + if (prop.value == ValueTypeFLOAT32 || + prop.value == ValueTypeFLOAT) { + readValue(prop.value, _in, tmp); + c[2] = static_cast (tmp * 255.0f); + } else + readInteger(prop.value, _in, c[2]); + break; + case COLORALPHA: + if (prop.value == ValueTypeFLOAT32 || + prop.value == ValueTypeFLOAT) { + readValue(prop.value, _in, tmp); + c[3] = static_cast (tmp * 255.0f); + } else + readInteger(prop.value, _in, c[3]); + break; case CUSTOM_PROP: if (_opt.check(Options::Custom) && fh.is_valid()) readCustomProperty(_in, _bi, fh, prop.name, prop.value, prop.listIndexType); @@ -681,6 +753,8 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap break; } } + if (_opt.face_has_color()) + _bi.set_color(fh, Vec4uc(c)); } } else { @@ -1347,6 +1421,30 @@ bool _PLYReader_::can_u_read(std::istream& _is) const { options_ += Options::ColorFloat; } } + else if (elementName == "face") { + if (propertyName == "red") { + entry = PropertyInfo(COLORRED, valueType); + options_ += Options::FaceColor; + if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32) + options_ += Options::ColorFloat; + } else if (propertyName == "green") { + entry = PropertyInfo(COLORGREEN, valueType); + options_ += Options::FaceColor; + if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32) + options_ += Options::ColorFloat; + } else if (propertyName == "blue") { + entry = PropertyInfo(COLORBLUE, valueType); + options_ += Options::FaceColor; + if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32) + options_ += Options::ColorFloat; + } else if (propertyName == "alpha") { + entry = PropertyInfo(COLORALPHA, valueType); + options_ += Options::FaceColor; + options_ += Options::ColorAlpha; + if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32) + options_ += Options::ColorFloat; + } + } //not a special property, load as custom if (entry.value == Unsupported){ diff --git a/src/OpenMesh/Core/IO/writer/PLYWriter.cc b/src/OpenMesh/Core/IO/writer/PLYWriter.cc index 0b2ab876..49db3ec6 100644 --- a/src/OpenMesh/Core/IO/writer/PLYWriter.cc +++ b/src/OpenMesh/Core/IO/writer/PLYWriter.cc @@ -122,14 +122,6 @@ write(std::ostream& _os, BaseExporter& _be, Options _opt, std::streamsize _preci omerr() << "[PLYWriter] : Warning: Face normals are not supported and thus not exported! " << std::endl; } - if ( _opt.check(Options::FaceColor) ) { - // Face normals are not supported - // Uncheck these options and output message that - // they are not written out even though they were requested - _opt.unset(Options::FaceColor); - omerr() << "[PLYWriter] : Warning: Face colors are not supported and thus not exported! " << std::endl; - } - options_ = _opt; @@ -313,6 +305,24 @@ void _PLYWriter_::write_header(std::ostream& _out, BaseExporter& _be, Options& _ _out << "element face " << _be.n_faces() << '\n'; _out << "property list uchar int vertex_indices" << '\n'; + if ( _opt.face_has_color() ){ + if ( _opt.color_is_float() ) { + _out << "property float red" << '\n'; + _out << "property float green" << '\n'; + _out << "property float blue" << '\n'; + + if ( _opt.color_has_alpha() ) + _out << "property float alpha" << '\n'; + } else { + _out << "property uchar red" << '\n'; + _out << "property uchar green" << '\n'; + _out << "property uchar blue" << '\n'; + + if ( _opt.color_has_alpha() ) + _out << "property uchar alpha" << '\n'; + } + } + _ofProps = writeCustomTypeHeader(_out, _be.kernel()->fprops_begin(), _be.kernel()->fprops_end()); _out << "end_header" << '\n'; @@ -335,6 +345,7 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const OpenMesh::Vec4f cAf; OpenMesh::Vec2f t; VertexHandle vh; + FaceHandle fh; std::vector vhandles; std::vector vProps; @@ -400,12 +411,37 @@ write_ascii(std::ostream& _out, BaseExporter& _be, Options _opt) const // faces (indices starting at 0) for (i=0, nF=int(_be.n_faces()); i::iterator iter = fProps.begin(); iter < fProps.end(); ++iter) write_customProp(_out,*iter,i); @@ -562,6 +598,7 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const OpenMesh::Vec4uc c; OpenMesh::Vec4f cf; VertexHandle vh; + FaceHandle fh; std::vector vhandles; // vProps and fProps will be empty, until custom properties are supported by the binary writer @@ -624,12 +661,35 @@ write_binary(std::ostream& _out, BaseExporter& _be, Options _opt) const for (i=0, nF=int(_be.n_faces()); i::iterator iter = fProps.begin(); iter < fProps.end(); ++iter) write_customProp(_out,*iter,i); } diff --git a/src/OpenMesh/Core/IO/writer/PLYWriter.hh b/src/OpenMesh/Core/IO/writer/PLYWriter.hh index fe2b8212..d11571ca 100644 --- a/src/OpenMesh/Core/IO/writer/PLYWriter.hh +++ b/src/OpenMesh/Core/IO/writer/PLYWriter.hh @@ -82,6 +82,7 @@ namespace IO { currently supported options: - VertexColors + - FaceColors - Binary - Binary -> MSB */ diff --git a/src/Unittests/TestFiles/cube-minimal-faceColors.ply b/src/Unittests/TestFiles/cube-minimal-faceColors.ply new file mode 100644 index 00000000..4b97743e --- /dev/null +++ b/src/Unittests/TestFiles/cube-minimal-faceColors.ply @@ -0,0 +1,33 @@ +ply +format ascii 1.0 +comment VCGLIB generated +element vertex 8 +property float x +property float y +property float z +element face 12 +property list uchar int vertex_indices +property float red +property float green +property float blue +end_header +-1 -1 -1 +1 -1 -1 +1 1 -1 +-1 1 -1 +-1 -1 1 +1 -1 1 +1 1 1 +-1 1 1 +3 0 1 2 0.419608 0.462745 0.698039 +3 0 2 3 1.0 0.552941 0.419608 +3 5 4 7 0.698039 1.0 0.619608 +3 5 7 6 0.419608 1.0 0.529412 +3 6 2 1 0.643137 0.419608 0.698039 +3 6 1 5 0.643137 0.419608 1.0 +3 3 7 4 0.698039 0.552941 1.0 +3 3 4 0 1.0 0.552941 0.419608 +3 7 3 2 0.419608 0.698039 1.0 +3 7 2 6 0.419608 0.698039 0.529412 +3 5 1 0 1.0 0.419608 1.0 +3 5 0 4 0.698039 1.0 1.0 diff --git a/src/Unittests/unittests_read_write_PLY.cc b/src/Unittests/unittests_read_write_PLY.cc index dd83ff81..bd1a4533 100644 --- a/src/Unittests/unittests_read_write_PLY.cc +++ b/src/Unittests/unittests_read_write_PLY.cc @@ -800,4 +800,143 @@ TEST_F(OpenMeshReadWritePLY, FailOnUnknownPropertyTypeForLists) { EXPECT_EQ(0u, mesh_.n_faces()) << "The number of loaded faces is not correct!"; } +/* + * Load an ASCII PLY file of a cube with float RGB face colors + */ +TEST_F(OpenMeshReadWritePLY, LoadSimplePLYWithFaceColors) { + + mesh_.clear(); + + mesh_.request_face_colors(); + + OpenMesh::IO::Options options; + options += OpenMesh::IO::Options::FaceColor; + + bool ok = OpenMesh::IO::read_mesh(mesh_, "cube-minimal-faceColors.ply",options); + + EXPECT_TRUE(ok) << "Unable to load cube-minimal-faceColors.ply"; + + EXPECT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!"; + EXPECT_EQ(18u, mesh_.n_edges()) << "The number of loaded edges is not correct!"; + EXPECT_EQ(12u, mesh_.n_faces()) << "The number of loaded faces is not correct!"; + + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(0))[0] ) << "Wrong face color at face 0"; + EXPECT_EQ(117, mesh_.color(mesh_.face_handle(0))[1] ) << "Wrong face color at face 0"; + EXPECT_EQ(177, mesh_.color(mesh_.face_handle(0))[2] ) << "Wrong face color at face 0"; + + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(3))[0] ) << "Wrong face color at face 3"; + EXPECT_EQ(255, mesh_.color(mesh_.face_handle(3))[1] ) << "Wrong face color at face 3"; + EXPECT_EQ(135, mesh_.color(mesh_.face_handle(3))[2] ) << "Wrong face color at face 3"; + + EXPECT_EQ(163, mesh_.color(mesh_.face_handle(4))[0] ) << "Wrong face color at face 4"; + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(4))[1] ) << "Wrong face color at face 4"; + EXPECT_EQ(177, mesh_.color(mesh_.face_handle(4))[2] ) << "Wrong face color at face 4"; + + EXPECT_EQ(255, mesh_.color(mesh_.face_handle(7))[0] ) << "Wrong face color at face 7"; + EXPECT_EQ(140, mesh_.color(mesh_.face_handle(7))[1] ) << "Wrong face color at face 7"; + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(7))[2] ) << "Wrong face color at face 7"; + + EXPECT_FALSE(options.vertex_has_normal()) << "Wrong user options are returned!"; + EXPECT_FALSE(options.vertex_has_texcoord()) << "Wrong user options are returned!"; + EXPECT_FALSE(options.vertex_has_color()) << "Wrong user options are returned!"; + EXPECT_TRUE(options.face_has_color()) << "Wrong user options are returned!"; + EXPECT_FALSE(options.color_has_alpha()) << "Wrong user options are returned!"; + EXPECT_FALSE(options.is_binary()) << "Wrong user options are returned!"; + + mesh_.release_face_colors(); +} + +/* + * Write and read PLY files with face colors in various formats + */ +TEST_F(OpenMeshReadWritePLY, WriteAndReadPLYWithFaceColors) { + struct Format { + Format(const char* outFileName, OpenMesh::IO::Options options) : + _outFileName(outFileName), + _options(options) + {} + const char* _outFileName; + const OpenMesh::IO::Options _options; + } + formats[] = + { + Format("cube-minimal-faceColors_ascii_uchar.ply", + OpenMesh::IO::Options::Default), + Format("cube-minimal-faceColors_ascii_float.ply", + OpenMesh::IO::Options::ColorFloat), + Format("cube-minimal-faceColors_binary_uchar.ply", + OpenMesh::IO::Options::Binary), + Format("cube-minimal-faceColors_binary_float.ply", + OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::ColorFloat), + // Test writing/reading alpha values (all default 1.0/255), but the test mesh + // Color type has no alpha channel so there's nothing to test below + Format("cube-minimal-faceColors_alpha_ascii_uchar.ply", + OpenMesh::IO::Options::ColorAlpha), + Format("cube-minimal-faceColors_alpha_ascii_float.ply", + OpenMesh::IO::Options::ColorFloat | OpenMesh::IO::Options::ColorAlpha), + Format("cube-minimal-faceColors_alpha_binary_uchar.ply", + OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::ColorAlpha), + Format("cube-minimal-faceColors_alpha_binary_float.ply", + OpenMesh::IO::Options::Binary | OpenMesh::IO::Options::ColorFloat | OpenMesh::IO::Options::ColorAlpha), + }; + + for (size_t i = 0; i < sizeof(formats) / sizeof(Format); ++i) + { + const char* outFileName = formats[i]._outFileName; + + mesh_.clear(); + + mesh_.request_face_colors(); + + OpenMesh::IO::Options options; + options += OpenMesh::IO::Options::FaceColor; + + bool ok = OpenMesh::IO::read_mesh(mesh_, "cube-minimal-faceColors.ply", options); + + EXPECT_TRUE(ok) << "Unable to load cube-minimal-faceColors.ply"; + + options = formats[i]._options; + options += OpenMesh::IO::Options::FaceColor; + ok = OpenMesh::IO::write_mesh(mesh_, outFileName, options); + EXPECT_TRUE(ok) << "Unable to write " << outFileName; + + // Reset for reading: let the reader determine binary/float/etc. + options = OpenMesh::IO::Options::FaceColor; + mesh_.clear(); + ok = OpenMesh::IO::read_mesh(mesh_, outFileName, options); + EXPECT_TRUE(ok) << "Unable to load " << outFileName; + + EXPECT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct: " << outFileName; + EXPECT_EQ(18u, mesh_.n_edges()) << "The number of loaded edges is not correct: " << outFileName; + EXPECT_EQ(12u, mesh_.n_faces()) << "The number of loaded faces is not correct: " << outFileName; + + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(0))[0] ) << "Wrong face color at face 0: " << outFileName; + EXPECT_EQ(117, mesh_.color(mesh_.face_handle(0))[1] ) << "Wrong face color at face 0: " << outFileName; + EXPECT_EQ(177, mesh_.color(mesh_.face_handle(0))[2] ) << "Wrong face color at face 0: " << outFileName; + + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(3))[0] ) << "Wrong face color at face 3: " << outFileName; + EXPECT_EQ(255, mesh_.color(mesh_.face_handle(3))[1] ) << "Wrong face color at face 3: " << outFileName; + EXPECT_EQ(135, mesh_.color(mesh_.face_handle(3))[2] ) << "Wrong face color at face 3: " << outFileName; + + EXPECT_EQ(163, mesh_.color(mesh_.face_handle(4))[0] ) << "Wrong face color at face 4: " << outFileName; + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(4))[1] ) << "Wrong face color at face 4: " << outFileName; + EXPECT_EQ(177, mesh_.color(mesh_.face_handle(4))[2] ) << "Wrong face color at face 4: " << outFileName; + + EXPECT_EQ(255, mesh_.color(mesh_.face_handle(7))[0] ) << "Wrong face color at face 7: " << outFileName; + EXPECT_EQ(140, mesh_.color(mesh_.face_handle(7))[1] ) << "Wrong face color at face 7: " << outFileName; + EXPECT_EQ(107, mesh_.color(mesh_.face_handle(7))[2] ) << "Wrong face color at face 7: " << outFileName; + + EXPECT_FALSE(options.vertex_has_normal()) << "Wrong user options are returned: " << outFileName; + EXPECT_FALSE(options.vertex_has_texcoord()) << "Wrong user options are returned: " << outFileName; + EXPECT_FALSE(options.vertex_has_color()) << "Wrong user options are returned: " << outFileName; + EXPECT_TRUE(options.face_has_color()) << "Wrong user options are returned: " << outFileName; + EXPECT_EQ(formats[i]._options.color_is_float(), options.color_is_float()) << + "Wrong user options are returned: " << outFileName; + EXPECT_EQ(formats[i]._options.is_binary(), options.is_binary()) << + "Wrong user options are returned: " << outFileName; + + mesh_.release_face_colors(); + } +} + } -- GitLab From 2c0ba7b7ebbc9c03f8778a88f8f22ac6c146dc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Thu, 21 Feb 2019 12:54:25 +0100 Subject: [PATCH 2/2] Change tutorial, as PLY now supports face colors --- Doc/tutorial_08.docu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial_08.docu b/Doc/tutorial_08.docu index 394e81d5..2a221644 100644 --- a/Doc/tutorial_08.docu +++ b/Doc/tutorial_08.docu @@ -55,7 +55,7 @@ ASCII is not a real option and will be selected, if binary was not defined. Format/OptionASCIIBinaryMSBLSBSwapVertexNormalVertexColorVertexTexCoordEdgeColorFaceNormalFaceColorFaceTexCoordColorAlphaColorFloatCustom OBJx xx *)x xxx OFFxx x xxx x xx -PLYxxxx xxx xxx **) +PLYxxxx xxx x xxx **) OM xxxxxxxxxx (\ref tutorial_09 ) STLxxxx VTK ***)x -- GitLab