diff --git a/.gitignore b/.gitignore index c8fa089db..68b6e8de4 100644 --- a/.gitignore +++ b/.gitignore @@ -122,5 +122,7 @@ tools/assimp_qt_viewer/ui_mainwindow.h generated/* # 3rd party cloned repos/tarballs etc +# meshlab repo, automatically cloned via CMake (to gain 2 source files for VRML file format conversion) +contrib/meshlab/autoclone # tinyusdz repo, automatically cloned via CMake contrib/tinyusdz/autoclone diff --git a/CMakeLists.txt b/CMakeLists.txt index fb1e0d1d8..fff7cf855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,12 +40,47 @@ SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW) CMAKE_MINIMUM_REQUIRED( VERSION 3.22 ) +#================================================================================# +# Model formats not enabled by default +# +# 3rd party projects may not adhere to strict standards enforced by assimp, +# in which case those formats must be opt-in; otherwise the 3rd party code +# would fail assimp CI checks +#================================================================================# +# M3D format import support (assimp integration no longer supported by M3D format author) +# User may override these in their CMake script to provide M3D import/export support +# (M3D importer/exporter was disabled for assimp release 5.1 or later) +option(ASSIMP_BUILD_M3D_IMPORTER "Enable M3D file import" off) +option(ASSIMP_BUILD_M3D_EXPORTER "Enable M3D file export" off) + # Experimental USD importer: disabled, need to opt-in # Note: assimp github PR automatic checks will fail the PR due to compiler warnings in # the external, 3rd party tinyusdz code which isn't technically part of the PR since it's # auto-cloned during build; so MUST disable the feature or the PR will be rejected option(ASSIMP_BUILD_USD_IMPORTER "Enable USD file import" off) option(ASSIMP_BUILD_USD_VERBOSE_LOGS "Enable verbose USD import debug logging" off) + +# VRML (.wrl/.x3dv) file import support by leveraging X3D importer and 3rd party file +# format converter to convert .wrl/.x3dv files to X3D-compatible .xml +# (Need to make this opt-in because 3rd party code triggers lots of CI code quality warnings) +option(ASSIMP_BUILD_VRML_IMPORTER "Enable VRML (.wrl/.x3dv) file import" off) +#--------------------------------------------------------------------------------# +# Internal impl for optional model formats +#--------------------------------------------------------------------------------# +# Internal/private M3D logic +if (NOT ASSIMP_BUILD_M3D_IMPORTER) + ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER) +endif () # if (not ASSIMP_BUILD_M3D_IMPORTER) +if (NOT ASSIMP_BUILD_M3D_EXPORTER) + ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER) +endif () # if (not ASSIMP_BUILD_M3D_EXPORTER) + +# Internal/private VRML logic +if (NOT ASSIMP_BUILD_VRML_IMPORTER) + ADD_DEFINITIONS( -DASSIMP_BUILD_NO_VRML_IMPORTER) +endif () # if (not ASSIMP_BUILD_VRML_IMPORTER) +#================================================================================# + option(ASSIMP_BUILD_USE_CCACHE "Use ccache to speed up compilation." on) if(ASSIMP_BUILD_USE_CCACHE) @@ -56,19 +91,6 @@ if(ASSIMP_BUILD_USE_CCACHE) endif() endif() -# User may override these in their CMake script to provide M3D import/export support -# (M3D importer/exporter was disabled for assimp release 5.1 or later) -option(ASSIMP_BUILD_M3D_IMPORTER "Enable M3D file import" off) -option(ASSIMP_BUILD_M3D_EXPORTER "Enable M3D file export" off) - -# Internal/private M3D logic -if (NOT ASSIMP_BUILD_M3D_IMPORTER) - ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER) -endif () # if (not ASSIMP_BUILD_M3D_IMPORTER) -if (NOT ASSIMP_BUILD_M3D_EXPORTER) - ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER) -endif () # if (not ASSIMP_BUILD_M3D_EXPORTER) - # Toggles the use of the hunter package manager option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) diff --git a/code/AssetLib/VRML/README.md b/code/AssetLib/VRML/README.md new file mode 100644 index 000000000..be045804e --- /dev/null +++ b/code/AssetLib/VRML/README.md @@ -0,0 +1,21 @@ +# WRL/X3DV to X3D file format converter + +## VRML and X3D 3D model formats background +"VRML" 3D model files use either `VRML97` (`.wrl`) or "Classic VRML" (`.x3dv`) +file formats. + +The X3D model specification was introduced after these formats, as a superset of both WRL and X3DV. +While X3D can understand the _content_ of WRL/X3DV files, it can't directly parse them because +X3D uses `.xml` files, rather than `VRML97` or "Classic VRML" format. + +But, if a converter is available to migrate just the file format (preserving the content), so that +the `.wrl`/`.x3dv` files can be converted to an X3D-compatible `.xml` file, then the X3D importer +will be able to load the resulting model file. + +## How this code is used +The sole purpose of `Parser`/`Scanner` (adopted from the `meshlab` project) is to take a +`VRML97` (`.wrl`) or "Classic VRML" (`.x3dv`) file as input, and convert to an X3D `.xml` file. +That's it. + +By passing the converted in-memory `.xml` file content to the `X3DImporter`, the `.wrl` or `x3dv` +model can be loaded via assimp. diff --git a/code/AssetLib/VRML/VrmlConverter.cpp b/code/AssetLib/VRML/VrmlConverter.cpp new file mode 100644 index 000000000..c5db2d011 --- /dev/null +++ b/code/AssetLib/VRML/VrmlConverter.cpp @@ -0,0 +1,103 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* 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. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +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 +OWNER 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. + +---------------------------------------------------------------------- +*/ +/// \file VrmlImporter.cpp +/// \brief Convert VRML-formatted (.wrl, .x3dv) files to X3D .xml format +/// \date 2024 +/// \author tellypresence + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include // std::unique_ptr +#include "VrmlConverter.hpp" + +namespace Assimp { + +bool isFileWrlVrml97Ext(const std::string &pFile) { + size_t pos = pFile.find_last_of('.'); + if (pos == std::string::npos) { + return false; + } + std::string ext = pFile.substr(pos + 1); + if (ext.size() != 3) { + return false; + } + return (ext[0] == 'w' || ext[0] == 'W') && (ext[1] == 'r' || ext[1] == 'R') && (ext[2] == 'l' || ext[2] == 'L'); +} + +bool isFileX3dvClassicVrmlExt(const std::string &pFile) { + size_t pos = pFile.find_last_of('.'); + if (pos == std::string::npos) { + return false; + } + std::string ext = pFile.substr(pos + 1); + if (ext.size() != 4) { + return false; + } + return (ext[0] == 'x' || ext[0] == 'X') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'v' || ext[3] == 'V'); +} + +#if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER) +static VrmlTranslator::Scanner createScanner(const std::string &pFile) { + std::unique_ptr wide_stringPtr{ new wchar_t[ pFile.length() + 1 ] }; + std::copy(pFile.begin(), pFile.end(), wide_stringPtr.get()); + wide_stringPtr[ pFile.length() ] = 0; + + return VrmlTranslator::Scanner(wide_stringPtr.get()); +} // wide_stringPtr auto-deleted when leaving scope +#endif // #if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER) + +std::stringstream ConvertVrmlFileToX3dXmlFile(const std::string &pFile) { + std::stringstream ss; + if (isFileWrlVrml97Ext(pFile) || isFileX3dvClassicVrmlExt(pFile)) { +#if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER) + VrmlTranslator::Scanner scanner = createScanner(pFile); + VrmlTranslator::Parser parser(&scanner); + parser.Parse(); + ss.str(""); + parser.doc_.save(ss); +#endif // #if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER) + } + return ss; +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/VRML/VrmlConverter.hpp b/code/AssetLib/VRML/VrmlConverter.hpp new file mode 100644 index 000000000..3d6234723 --- /dev/null +++ b/code/AssetLib/VRML/VrmlConverter.hpp @@ -0,0 +1,57 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2024, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* 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. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +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 +OWNER 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. + +---------------------------------------------------------------------- +*/ + +#pragma once + +#include +#include + +#if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER) +#include "contrib/meshlab/autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Parser.h" +#endif // #if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER) + +namespace Assimp { + +bool isFileWrlVrml97Ext(const std::string &pFile); +bool isFileX3dvClassicVrmlExt(const std::string &pFile); + +std::stringstream ConvertVrmlFileToX3dXmlFile(const std::string &pFile); +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index ada388080..8fb44f014 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER +#include "AssetLib/VRML/VrmlConverter.hpp" #include "X3DImporter.hpp" #include "X3DImporter_Macro.hpp" @@ -54,11 +55,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#if defined(ASSIMP_BUILD_NO_VRML_IMPORTER) +#define X3D_FORMATS_DESCR_STR "Extensible 3D(X3D, X3DB) Importer" +#define X3D_FORMATS_EXTENSIONS_STR "x3d x3db" +#else +#define X3D_FORMATS_DESCR_STR "VRML(WRL, X3DV) and Extensible 3D(X3D, X3DB) Importer" +#define X3D_FORMATS_EXTENSIONS_STR "wrl x3d x3db x3dv" +#endif // #if defined(ASSIMP_BUILD_NO_VRML_IMPORTER) + namespace Assimp { /// Constant which holds the importer description const aiImporterDesc X3DImporter::Description = { - "Extensible 3D(X3D) Importer", + X3D_FORMATS_DESCR_STR, "smalcom", "", "See documentation in source code. Chapter: Limitations.", @@ -67,7 +76,7 @@ const aiImporterDesc X3DImporter::Description = { 0, 0, 0, - "x3d x3db" + X3D_FORMATS_EXTENSIONS_STR }; bool X3DImporter::isNodeEmpty(XmlNode &node) { @@ -215,7 +224,19 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { if (!theParser.parse(fileStream.get())) { return; } + ParseFile(theParser); +} +void X3DImporter::ParseFile(std::istream &myIstream) { + XmlParser theParser; + if (!theParser.parse(myIstream)) { + LogInfo("ParseFile(): ERROR: failed to convert VRML istream to xml"); + return; + } + ParseFile(theParser); +} + +void X3DImporter::ParseFile(XmlParser &theParser) { XmlNode *node = theParser.findNode("X3D"); if (nullptr == node) { return; @@ -246,9 +267,13 @@ void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy mpIOHandler = pIOHandler; Clear(); - std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); - if (!stream) { - throw DeadlyImportError("Could not open file for reading"); + std::stringstream ss = ConvertVrmlFileToX3dXmlFile(pFile); + const bool isReadFromMem{ ss.str().length() > 0 }; + if (!isReadFromMem) { + std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); + if (!stream) { + throw DeadlyImportError("Could not open file for reading"); + } } std::string::size_type slashPos = pFile.find_last_of("\\/"); @@ -257,9 +282,13 @@ void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy pScene->mRootNode->mParent = nullptr; pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; - pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); - ParseFile(pFile, pIOHandler); - pIOHandler->PopDirectory(); + if (isReadFromMem) { + ParseFile(ss); + } else { + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + } //search for root node element diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 623160a38..da4888c77 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -275,7 +275,9 @@ public: /// Also exception can be thrown if trouble will found. /// \param [in] pFile - name of file to be parsed. /// \param [in] pIOHandler - pointer to IO helper object. - void ParseFile(const std::string &pFile, IOSystem *pIOHandler); + void ParseFile(const std::string &file, IOSystem *pIOHandler); + void ParseFile(std::istream &myIstream); + void ParseFile(XmlParser &theParser); bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const; void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 304235d80..c5f8ed9ef 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -864,6 +864,7 @@ ADD_ASSIMP_IMPORTER( X3D AssetLib/X3D/X3DGeoHelper.h AssetLib/X3D/X3DXmlHelper.cpp AssetLib/X3D/X3DXmlHelper.h + AssetLib/VRML/VrmlConverter.cpp ) ADD_ASSIMP_IMPORTER( GLTF @@ -930,6 +931,69 @@ SET( Extra_SRCS ) SOURCE_GROUP( Extra FILES ${Extra_SRCS}) +# VRML (.wrl/.x3dv) support +IF (ASSIMP_BUILD_VRML_IMPORTER) + # Note: ALWAYS specify a git commit hash (or tag) instead of a branch name; using a branch name + # can lead to non-deterministic (unpredictable) results since the code is potentially in flux + # "main" branch, 18 Nov 2024 + set(Meshlab_GIT_TAG "ad55b47a9b0700e7b427db6db287bb3a39aa31e7") + message("****") + message("\n\n**** Cloning meshlab repo, git tag ${Meshlab_GIT_TAG}\n\n") + + # Use CMAKE_CURRENT_SOURCE_DIR which provides assimp-local path (CMAKE_SOURCE_DIR is + # relative to top-level/main project) + set(Meshlab_BASE_ABSPATH "${CMAKE_CURRENT_SOURCE_DIR}/../contrib/meshlab") + + # Note: Depending on user's OS, build environment etc it may be necessary to change line endings of + # "patches/meshlab.patch" file from CRLF to LF in order for patch operation to succeed + # Patch required to + # - replace QtXml w/pugixml + # - disable meshlab cmake scripts to prevent breaking assimp build + # - address compiler warnings to avoid breaking build for users who wisely treat warnings-as-errors + set(Meshlab_PATCH_CMD git apply ${Meshlab_BASE_ABSPATH}/patches/meshlab.patch) + + # Note: cloning entire meshlab repo is wasteful since we require literally only two source files. + # There is a technique using "git archive" e.g. + # execute_process( + # COMMAND git archive --remote=${Meshlab_GIT_REPO} ${Meshlab_GIT_TAG} | \ + # tar -x -C ${Meshlab_BASE_ABSPATH} + # RESULT_VARIABLE result + # ) + # But this doesn't work with git "https" protocol so not a viable solution + + # Note: CMake's "FetchContent" (which executes at configure time) is much better for this use case + # than "ExternalProject" (which executes at build time); we just want to clone a repo and + # block (wait) as long as necessary until cloning is complete, so we immediately have full + # access to the cloned source files + include(FetchContent) + set(Meshlab_REPO_ABSPATH "${Meshlab_BASE_ABSPATH}/autoclone") + # Only want to clone once (on Android, using SOURCE_DIR will clone per-ABI (x86, x86_64 etc)) + set(FETCHCONTENT_BASE_DIR ${Meshlab_REPO_ABSPATH}) + set(FETCHCONTENT_QUIET on) # Turn off to troubleshoot repo clone problems + set(FETCHCONTENT_UPDATES_DISCONNECTED on) # Prevent other ABIs from re-cloning/re-patching etc + set(Meshlab_GIT_REPO "https://github.com/cnr-isti-vclab/meshlab") + FetchContent_Declare( + meshlab_repo + GIT_REPOSITORY ${Meshlab_GIT_REPO} + GIT_TAG ${Meshlab_GIT_TAG} + PATCH_COMMAND ${Meshlab_PATCH_CMD} + ) + FetchContent_MakeAvailable(meshlab_repo) + message("**** Finished cloning meshlab repo") + message("****") + set(Meshlab_SRC_ABSPATH "${Meshlab_REPO_ABSPATH}/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml") + set(Meshlab_SRCS + ${Meshlab_SRC_ABSPATH}/Parser.cpp + ${Meshlab_SRC_ABSPATH}/Scanner.cpp + ) + set(Meshlab_INCLUDE_DIRS "${Meshlab_SRC_ABSPATH}") + INCLUDE_DIRECTORIES(${Meshlab_INCLUDE_DIRS}) + MESSAGE(STATUS "VRML enabled") +ELSE() # IF (ASSIMP_BUILD_VRML_IMPORTER) + set(Meshlab_SRCS "") + MESSAGE(STATUS "VRML disabled") +ENDIF() # IF (ASSIMP_BUILD_VRML_IMPORTER) + # USD/USDA/USDC/USDZ support # tinyusdz IF (ASSIMP_BUILD_USD_IMPORTER) @@ -1297,6 +1361,7 @@ SET( assimp_src ${openddl_parser_SRCS} ${open3dgc_SRCS} ${ziplib_SRCS} + ${Meshlab_SRCS} ${Tinyusdz_SRCS} ${Tinyusdz_DEP_SOURCES} ${Pugixml_SRCS} diff --git a/contrib/meshlab/README.md b/contrib/meshlab/README.md new file mode 100644 index 000000000..13a06bda6 --- /dev/null +++ b/contrib/meshlab/README.md @@ -0,0 +1,11 @@ +# meshlab +Meshlab project cloned in entirety but only using two files: "Parser" and "Scanner" in order to +reformat .wrl/.x3dv files as .xml + +## Automatic repo clone +Meshlab repo is automatically cloned. Users who haven't opted-in to VRML support +won't be burdened with the extra download volume. + +To update the git commit hash pulled down, modify `Meshlab_GIT_TAG` in file +`code/CMakeLists.txt`; it is not expected that the sole files of interest "Parser" and "Scanner" +will change frequently, if at all, going forward diff --git a/contrib/meshlab/patches/README.md b/contrib/meshlab/patches/README.md new file mode 100644 index 000000000..992d7f57b --- /dev/null +++ b/contrib/meshlab/patches/README.md @@ -0,0 +1,14 @@ +# meshlab patch + +## Notes +Depending on user's OS, build environment etc it may be necessary to change line endings of +`patches/meshlab.patch` file from `CRLF` to `LF` in order for patch operation to succeed + +## Overview +"Parser" based on QtXml, need to change to use pugixml + +## pugixml notes +Note that it isn't possible to add an unattached pugixml Node object, modify it, then +add it to the tree later; the node needs to be attached somewhere in the tree on instantiation +(even if it's a temporary at root level to be removed when finished) before populating, adding +children etc. diff --git a/contrib/meshlab/patches/meshlab.patch b/contrib/meshlab/patches/meshlab.patch new file mode 100644 index 000000000..cb6763ef1 --- /dev/null +++ b/contrib/meshlab/patches/meshlab.patch @@ -0,0 +1,1191 @@ +diff -rupN -x .git autoclone/meshlab_repo-src/CMakeLists.txt meshlab_repo_patch/CMakeLists.txt +--- autoclone/meshlab_repo-src/CMakeLists.txt 2024-12-06 18:01:10.831623800 -0800 ++++ meshlab_repo_patch/CMakeLists.txt 2024-12-06 18:15:09.647799500 -0800 +@@ -16,4 +16,6 @@ option(MESHLAB_USE_DEFAULT_BUILD_AND_INS + + option(MESHLAB_IS_NIGHTLY_VERSION "Nightly version of meshlab will be used instead of ML_VERSION" OFF) + +-add_subdirectory(src) +\ No newline at end of file ++# Disable meshlab project to avoid breaking assimp build (for converting VMRL (.wrl/.x3dv) files to ++# .xml format, just need to compile two source files) ++#add_subdirectory(src) +diff -rupN -x .git autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Parser.cpp meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Parser.cpp +--- autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Parser.cpp 2024-12-06 18:01:12.268963700 -0800 ++++ meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Parser.cpp 2024-12-06 14:19:26.138842300 -0800 +@@ -32,11 +32,11 @@ + + *****************************************************************************/ + ++#include + #include + #include "Parser.h" + #include "Scanner.h" + +- + namespace VrmlTranslator { + + +@@ -95,9 +95,11 @@ bool Parser::WeakSeparator(int n, int sy + } + + void Parser::VrmlTranslator() { +- QDomElement root = doc->createElement("X3D"); +- QDomElement scene = doc->createElement("Scene"); +- root.appendChild(scene); ++// QDomElement root = doc->createElement("X3D"); ++ pugi::xml_node root = doc->append_child("X3D"); ++// QDomElement scene = doc->createElement("Scene"); ++// root.appendChild(scene); ++ pugi::xml_node scene = root.append_child("Scene"); + InitX3dNode(); + if (la->kind == 7) { + HeaderStatement(); +@@ -108,7 +110,7 @@ void Parser::VrmlTranslator() { + ComponentStatements(); + MetaStatements(); + Statements(scene); +- doc->appendChild(root); ++// doc->appendChild(root); + } + + void Parser::HeaderStatement() { +@@ -147,7 +149,7 @@ void Parser::MetaStatements() { + } + } + +-void Parser::Statements(QDomElement& parent) { ++void Parser::Statements(pugi::xml_node& parent) { + while (StartOf(1)) { + Statement(parent); + } +@@ -173,16 +175,16 @@ void Parser::ComponentSupportLevel() { + } + + void Parser::ExportStatement() { +- QString str; ++ std::string str; + Expect(14); + NodeNameId(str); + Expect(15); + ExportedNodeNameId(); + } + +-void Parser::NodeNameId(QString& str) { ++void Parser::NodeNameId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + + void Parser::ExportedNodeNameId() { +@@ -190,7 +192,7 @@ void Parser::ExportedNodeNameId() { + } + + void Parser::ImportStatement() { +- QString str; ++ std::string str; + Expect(16); + InlineNodeNameId(); + Expect(17); +@@ -217,7 +219,7 @@ void Parser::Metavalue() { + Expect(4); + } + +-void Parser::Statement(QDomElement& parent) { ++void Parser::Statement(pugi::xml_node& parent) { + if (StartOf(2)) { + NodeStatement(parent); + } else if (la->kind == 16) { +@@ -231,8 +233,8 @@ void Parser::Statement(QDomElement& pare + } else SynErr(87); + } + +-void Parser::NodeStatement(QDomElement& parent) { +- QString tagName, attrValue; ++void Parser::NodeStatement(pugi::xml_node& parent) { ++ std::string tagName, attrValue; + if (la->kind == 1 || la->kind == 38) { + Node(parent, tagName, ""); + } else if (la->kind == 19) { +@@ -242,17 +244,19 @@ void Parser::NodeStatement(QDomElement& + } else if (la->kind == 20) { + Get(); + NodeNameId(attrValue); +- std::map::const_iterator iter = defNode.find(attrValue); ++ std::map::const_iterator iter = defNode.find(attrValue); + if(iter != defNode.end()) + { +- QDomElement node = doc->createElement(iter->second); +- node.setAttribute("USE", attrValue); +- parent.appendChild(node); ++// QDomElement node = doc->createElement(iter->second); ++ pugi::xml_node node = parent.append_child((iter->second).c_str()); ++// node.setAttribute("USE", attrValue); ++ node.append_attribute("USE") = attrValue.c_str(); ++// parent.appendChild(node); + } + } else SynErr(88); + } + +-void Parser::ProtoStatement(QDomElement& parent) { ++void Parser::ProtoStatement(pugi::xml_node& parent) { + if (la->kind == 21) { + Proto(parent); + } else if (la->kind == 34) { +@@ -261,7 +265,7 @@ void Parser::ProtoStatement(QDomElement& + } + + void Parser::RouteStatement() { +- QString str; ++ std::string str; + Expect(35); + NodeNameId(str); + Expect(17); +@@ -272,22 +276,27 @@ void Parser::RouteStatement() { + InputOnlyId(str); + } + +-void Parser::Node(QDomElement& parent, QString& tagName, const QString defValue) { +- bool flag = false; QDomElement node; ++void Parser::Node(pugi::xml_node& parent, std::string& tagName, const std::string defValue) { ++ bool flag = false; pugi::xml_node node; + if (la->kind == 1) { + NodeTypeId(tagName); +- std::set::const_iterator iter = proto.find(tagName); ++ std::set::const_iterator iter = proto.find(tagName); + if (iter != proto.end()) + { +- node = doc->createElement("ProtoInstance"); +- node.setAttribute("name", tagName); ++// node = doc->createElement("ProtoInstance"); ++ node = parent.append_child("ProtoInstance"); ++// node.setAttribute("name", tagName); ++ node.append_attribute("name") = tagName.c_str(); + flag = true; + } +- else +- node = doc->createElement(tagName); ++ else { ++// node = doc->createElement(tagName); ++ node = parent.append_child(tagName.c_str()); ++ } + if (defValue != "") + { +- node.setAttribute("DEF", defValue); ++// node.setAttribute("DEF", defValue); ++ node.append_attribute("DEF") = defValue.c_str(); + defNode[defValue] = tagName; + } + Expect(24); +@@ -298,13 +307,14 @@ void Parser::Node(QDomElement& parent, Q + Expect(24); + ScriptBody(); + Expect(25); +- node = doc->createElement("Script"); ++// node = doc->createElement("Script"); ++ node = parent.append_child("Script"); + } else SynErr(90); +- parent.appendChild(node); ++// parent.appendChild(node); + } + +-void Parser::RootNodeStatement(QDomElement& parent) { +- QString tagName, attrValue; ++void Parser::RootNodeStatement(pugi::xml_node& parent) { ++ std::string tagName, attrValue; + if (la->kind == 1 || la->kind == 38) { + Node(parent, tagName, ""); + } else if (la->kind == 19) { +@@ -314,70 +324,82 @@ void Parser::RootNodeStatement(QDomEleme + } else SynErr(91); + } + +-void Parser::Proto(QDomElement& parent) { +- QString name; QDomElement node; ++void Parser::Proto(pugi::xml_node& parent) { ++// QString name; QDomElement node; ++ std::string name; pugi::xml_node node; + Expect(21); + NodeTypeId(name); +- node = doc->createElement("ProtoDeclare"); +- node.setAttribute("name", name); ++// node = doc->createElement("ProtoDeclare"); ++ node = parent.append_child("ProtoDeclare"); ++// node.setAttribute("name", name); ++ node.append_attribute("name") = name.c_str(); + proto.insert(name); + Expect(22); +- QDomElement interf = doc->createElement("ProtoInterface"); ++// QDomElement interf = doc->createElement("ProtoInterface"); ++ pugi::xml_node interf = node.append_child("ProtoInterface"); + InterfaceDeclarations(interf); +- node.appendChild(interf); ++// node.appendChild(interf); + Expect(23); + Expect(24); +- QDomElement body = doc->createElement("ProtoBody"); ++// QDomElement body = doc->createElement("ProtoBody"); ++ pugi::xml_node body = node.append_child("ProtoBody"); + ProtoBody(body); +- node.appendChild(body); ++// node.appendChild(body); + Expect(25); +- parent.appendChild(node); ++// parent.appendChild(node); + } + +-void Parser::Externproto(QDomElement& parent) { +- QString name, url; +- QDomElement node = doc->createElement("ExternProtoDeclare"); ++void Parser::Externproto(pugi::xml_node& parent) { ++// QString name, url; ++ std::string name, url; ++// QDomElement node = doc->createElement("ExternProtoDeclare"); ++ pugi::xml_node node = doc->append_child("ExternProtoDeclare"); + Expect(34); + NodeTypeId(name); + Expect(22); + ExternInterfaceDeclarations(node); + Expect(23); + URLList(url); +- std::set::const_iterator iter = x3dNode.find(name); ++ std::set::const_iterator iter = x3dNode.find(name); + if (iter == x3dNode.end()) + { +- node.setAttribute("name", name); +- node.setAttribute("url", url); +- parent.appendChild(node); ++// node.setAttribute("name", name); ++ node.append_attribute("name") = name.c_str(); ++// node.setAttribute("url", url); ++ node.append_attribute("url") = url.c_str(); ++// parent.appendChild(node); ++ parent.append_copy(node); + proto.insert(name); + } ++ doc->remove_child(node); + } + +-void Parser::ProtoStatements(QDomElement& parent) { ++void Parser::ProtoStatements(pugi::xml_node& parent) { + while (la->kind == 21 || la->kind == 34) { + ProtoStatement(parent); + } + } + +-void Parser::NodeTypeId(QString& str) { ++void Parser::NodeTypeId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::InterfaceDeclarations(QDomElement& parent) { ++void Parser::InterfaceDeclarations(pugi::xml_node& parent) { + while (StartOf(3)) { + InterfaceDeclaration(parent); + } + } + +-void Parser::ProtoBody(QDomElement& parent) { ++void Parser::ProtoBody(pugi::xml_node& parent) { + ProtoStatements(parent); + RootNodeStatement(parent); + Statements(parent); + } + +-void Parser::InterfaceDeclaration(QDomElement& parent) { +- QString name, type, val; QDomElement node; ++void Parser::InterfaceDeclaration(pugi::xml_node& parent) { ++// QString name, type, val; QDomElement node; ++ std::string name, type, val; pugi::xml_node node; + if (StartOf(4)) { + RestrictedInterfaceDeclaration(parent); + } else if (la->kind == 32 || la->kind == 33) { +@@ -388,18 +410,23 @@ void Parser::InterfaceDeclaration(QDomEl + } + FieldType(type); + FieldId(name); ++ node = parent.append_child("field"); + FieldValue(node, "value", false); +- node = doc->createElement("field"); +- node.setAttribute("name", name); +- node.setAttribute("type", type); +- node.setAttribute("accessType", "inputOutput"); +- parent.appendChild(node); ++// node = doc->createElement("field"); ++// node.setAttribute("name", name); ++ node.append_attribute("name") = name.c_str(); ++// node.setAttribute("type", type); ++ node.append_attribute("type") = type.c_str(); ++// node.setAttribute("accessType", "inputOutput"); ++ node.append_attribute("accessType") = "inputOutput"; ++// parent.appendChild(node); + } else SynErr(92); + } + +-void Parser::RestrictedInterfaceDeclaration(QDomElement& parent) { +- QString name; QString type; QString val; +- QDomElement node = doc->createElement("field"); ++void Parser::RestrictedInterfaceDeclaration(pugi::xml_node& parent) { ++ std::string name; std::string type; std::string val; ++// QDomElement node = doc->createElement("field"); ++ pugi::xml_node node = parent.append_child("field"); + if (la->kind == 26 || la->kind == 27) { + if (la->kind == 26) { + Get(); +@@ -408,7 +435,8 @@ void Parser::RestrictedInterfaceDeclarat + } + FieldType(type); + InputOnlyId(name); +- node.setAttribute("accessType", "inputOnly"); ++// node.setAttribute("accessType", "inputOnly"); ++ node.append_attribute("accessType") = "inputOnly"; + } else if (la->kind == 28 || la->kind == 29) { + if (la->kind == 28) { + Get(); +@@ -417,7 +445,8 @@ void Parser::RestrictedInterfaceDeclarat + } + FieldType(type); + OutputOnlyId(name); +- node.setAttribute("accessType", "outputOnly"); ++// node.setAttribute("accessType", "outputOnly"); ++ node.append_attribute("accessType") = "outputOnly"; + } else if (la->kind == 30 || la->kind == 31) { + if (la->kind == 30) { + Get(); +@@ -427,14 +456,17 @@ void Parser::RestrictedInterfaceDeclarat + FieldType(type); + InitializeOnlyId(name); + FieldValue(node, "value", false); +- node.setAttribute("accessType", "initializeOnly"); ++// node.setAttribute("accessType", "initializeOnly"); ++ node.append_attribute("accessType") = "initializeOnly"; + } else SynErr(93); +- node.setAttribute("name", name); +- node.setAttribute("type", type); +- parent.appendChild(node); ++// node.setAttribute("name", name); ++ node.append_attribute("name") = name.c_str(); ++// node.setAttribute("type", type); ++ node.append_attribute("type") = type.c_str(); ++// parent.appendChild(node); + } + +-void Parser::FieldType(QString& str) { ++void Parser::FieldType(std::string& str) { + switch (la->kind) { + case 40: { + Get(); +@@ -606,25 +638,25 @@ void Parser::FieldType(QString& str) { + } + default: SynErr(94); break; + } +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::InputOnlyId(QString& str) { ++void Parser::InputOnlyId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::OutputOnlyId(QString& str) { ++void Parser::OutputOnlyId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::InitializeOnlyId(QString& str) { ++void Parser::InitializeOnlyId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::FieldValue(QDomElement& parent, QString fieldName, bool flag) { ++void Parser::FieldValue(pugi::xml_node& parent, std::string fieldName, bool flag) { + if (StartOf(5)) { + SingleValue(parent, fieldName, flag); + } else if (la->kind == 22) { +@@ -632,21 +664,21 @@ void Parser::FieldValue(QDomElement& par + } else SynErr(95); + } + +-void Parser::FieldId(QString& str) { ++void Parser::FieldId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::ExternInterfaceDeclarations(QDomElement& parent) { ++void Parser::ExternInterfaceDeclarations(pugi::xml_node& parent) { + while (StartOf(3)) { + ExternInterfaceDeclaration(parent); + } + } + +-void Parser::URLList(QString& url) { ++void Parser::URLList(std::string& url) { + if (la->kind == 4) { + Get(); +- url = QString(coco_string_create_char(t->val)); ++ url = std::string(coco_string_create_char(t->val)); + } else if (la->kind == 22) { + Get(); + while (la->kind == 4) { +@@ -660,9 +692,10 @@ void Parser::URLList(QString& url) { + } else SynErr(96); + } + +-void Parser::ExternInterfaceDeclaration(QDomElement& parent) { +- QString type, name; +- QDomElement node = doc->createElement("field"); ++void Parser::ExternInterfaceDeclaration(pugi::xml_node& parent) { ++ std::string type, name; ++// QDomElement node = doc->createElement("field"); ++ pugi::xml_node node = parent.append_child("field"); + if (la->kind == 26 || la->kind == 27) { + if (la->kind == 26) { + Get(); +@@ -671,7 +704,8 @@ void Parser::ExternInterfaceDeclaration( + } + FieldType(type); + InputOnlyId(name); +- node.setAttribute("accessType", "inputOnly"); ++// node.setAttribute("accessType", "inputOnly"); ++ node.append_attribute("accessType") = "inputOnly"; + } else if (la->kind == 28 || la->kind == 29) { + if (la->kind == 28) { + Get(); +@@ -680,7 +714,8 @@ void Parser::ExternInterfaceDeclaration( + } + FieldType(type); + OutputOnlyId(name); +- node.setAttribute("accessType", "outputOnly"); ++// node.setAttribute("accessType", "outputOnly"); ++ node.append_attribute("accessType") = "outputOnly"; + } else if (la->kind == 30 || la->kind == 31) { + if (la->kind == 30) { + Get(); +@@ -689,7 +724,8 @@ void Parser::ExternInterfaceDeclaration( + } + FieldType(type); + InitializeOnlyId(name); +- node.setAttribute("accessType", "initializeOnly"); ++// node.setAttribute("accessType", "initializeOnly"); ++ node.append_attribute("accessType") = "initializeOnly"; + } else if (la->kind == 32 || la->kind == 33) { + if (la->kind == 32) { + Get(); +@@ -698,14 +734,17 @@ void Parser::ExternInterfaceDeclaration( + } + FieldType(type); + FieldId(name); +- node.setAttribute("accessType", "inputOutput"); ++// node.setAttribute("accessType", "inputOutput"); ++ node.append_attribute("accessType") = "inputOutput"; + } else SynErr(97); +- node.setAttribute("name" , name); +- node.setAttribute("type", type); +- parent.appendChild(node); ++// node.setAttribute("name" , name); ++ node.append_attribute("name") = name.c_str(); ++// node.setAttribute("type", type); ++ node.append_attribute("type") = type.c_str(); ++// parent.appendChild(node); + } + +-void Parser::NodeBody(QDomElement& parent, bool flag) { ++void Parser::NodeBody(pugi::xml_node& parent, bool flag) { + while (StartOf(6)) { + NodeBodyElement(parent, flag); + } +@@ -717,24 +756,29 @@ void Parser::ScriptBody() { + } + } + +-void Parser::NodeBodyElement(QDomElement& parent, bool flag) { +- QString idName, idProto; QDomElement node; ++void Parser::NodeBodyElement(pugi::xml_node& parent, bool flag) { ++// QString idName, idProto; QDomElement node; ++ std::string idName, idProto; pugi::xml_node node; + if (la->kind == 1) { + Get(); +- idName = QString(coco_string_create_char(t->val)); ++ idName = std::string(coco_string_create_char(t->val)); + if (StartOf(8)) { + FieldValue(parent, idName, flag); + } else if (la->kind == 39) { + Get(); + Expect(1); +- idProto = QString(coco_string_create_char(t->val)); +- node = doc->createElement("IS"); +- QDomElement connect = doc->createElement("connect"); +- connect.setAttribute("nodeField", idName); +- connect.setAttribute("protoField", idProto); +- node.appendChild(connect); +- parent.appendChild(node); +- ++ idProto = std::string(coco_string_create_char(t->val)); ++// node = doc->createElement("IS"); ++ node = parent.append_child("IS"); ++// QDomElement connect = doc->createElement("connect"); ++ pugi::xml_node connect = node.append_child("connect"); ++// connect.setAttribute("nodeField", idName); ++ connect.append_attribute("nodeField") = idName.c_str(); ++// connect.setAttribute("protoField", idProto); ++ connect.append_attribute("protoField") = idProto.c_str(); ++// node.appendChild(connect); ++// parent.appendChild(node); ++ + } else SynErr(98); + } else if (la->kind == 35) { + RouteStatement(); +@@ -744,7 +788,7 @@ void Parser::NodeBodyElement(QDomElement + } + + void Parser::ScriptBodyElement() { +- QString str; QDomElement elem; ++ std::string str; pugi::xml_node elem; + if (StartOf(6)) { + NodeBodyElement(elem, false); + } else if (la->kind == 26 || la->kind == 27) { +@@ -798,17 +842,21 @@ void Parser::ScriptBodyElement() { + } else SynErr(101); + } + +-void Parser::InputOutputId(QString& str) { ++void Parser::InputOutputId(std::string& str) { + Expect(1); +- str = QString(coco_string_create_char(t->val)); ++ str = std::string(coco_string_create_char(t->val)); + } + +-void Parser::SingleValue(QDomElement& parent, QString fieldName, bool flag) { +- QString value; QDomElement tmpParent = doc->createElement("tmp"); ++void Parser::SingleValue(pugi::xml_node& parent, std::string fieldName, bool flag) { ++// QString value; QDomElement tmpParent = doc->createElement("tmp"); ++ std::string value; pugi::xml_node tmpParent = doc->append_child("tmpParent"); + if (StartOf(9)) { + if (la->kind == 4) { + Get(); +- value.append(coco_string_create_char(t->val)); value.remove("\""); ++ value.append(coco_string_create_char(t->val)); //value.remove("\""); ++ // TODO: modify quotation removal; below violates const-correctness: ++ // error: cannot convert ‘std::__cxx11::basic_string::iterator’ to ‘const char*’ ++// value.erase(std::remove(value.begin(), value.end(), '"'), value.end()); + } else if (la->kind == 2 || la->kind == 3) { + if (la->kind == 2) { + Get(); +@@ -839,29 +887,41 @@ void Parser::SingleValue(QDomElement& pa + } + if (flag) + { +- QDomElement node = doc->createElement("fieldValue"); +- node.setAttribute("name", fieldName); +- node.setAttribute("value", value); +- parent.appendChild(node); +- } +- else +- parent.setAttribute(fieldName, value); ++// QDomElement node = doc->createElement("fieldValue"); ++ pugi::xml_node node = parent.append_child("fieldValue"); ++// node.setAttribute("name", fieldName); ++ node.append_attribute("name") = fieldName.c_str(); ++// node.setAttribute("value", value); ++ node.append_attribute("value") = value.c_str(); ++// parent.appendChild(node); ++ } ++ else { ++// parent.setAttribute(fieldName, value); ++ parent.append_attribute(fieldName.c_str()) = value.c_str(); ++ } + } else if (StartOf(2)) { + NodeStatement(tmpParent); + if (flag) + { +- QDomElement tmp = doc->createElement("fieldValue"); +- tmp.setAttribute("name", fieldName); +- tmp.appendChild(tmpParent.firstChildElement()); +- parent.appendChild(tmp); +- } +- else +- parent.appendChild(tmpParent.firstChildElement()); ++// QDomElement tmp = doc->createElement("fieldValue"); ++ pugi::xml_node tmp = parent.append_child("fieldValue"); ++// tmp.setAttribute("name", fieldName); ++ tmp.append_attribute("name") = fieldName.c_str(); ++// tmp.appendChild(tmpParent.firstChildElement()); ++ tmp.insert_child_before(pugi::node_element, tmpParent.first_child()); ++// parent.appendChild(tmp); ++ } ++ else { ++// parent.appendChild(tmpParent.firstChildElement()); ++ parent.append_copy(tmpParent.first_child()); ++ } + } else SynErr(102); ++ doc->remove_child(tmpParent); + } + +-void Parser::MultiValue(QDomElement& parent, QString fieldName, bool flag) { +- QString value; QDomElement tmpParent = doc->createElement("tmp"); ++void Parser::MultiValue(pugi::xml_node& parent, std::string fieldName, bool flag) { ++// QString value; QDomElement tmpParent = doc->createElement("tmp"); ++ std::string value; pugi::xml_node tmpParent = doc->append_child("tmpParent"); + Expect(22); + if (StartOf(10)) { + if (la->kind == 2 || la->kind == 3) { +@@ -873,14 +933,19 @@ void Parser::MultiValue(QDomElement& par + } + if (flag) + { +- QDomElement tmp = doc->createElement("fieldValue"); +- tmp.setAttribute("name", fieldName); +- tmp.setAttribute("value", value); +- parent.appendChild(tmp); +- } +- else +- parent.setAttribute(fieldName, value); +- ++// QDomElement tmp = doc->createElement("fieldValue"); ++ pugi::xml_node tmp = parent.append_child("fieldValue"); ++// tmp.setAttribute("name", fieldName); ++ tmp.append_attribute("name") = fieldName.c_str(); ++// tmp.setAttribute("value", value); ++ tmp.append_attribute("value") = value.c_str(); ++// parent.appendChild(tmp); ++ } ++ else { ++// parent.setAttribute(fieldName, value); ++ parent.append_attribute(fieldName.c_str()) = value.c_str(); ++ } ++ + } else if (StartOf(11)) { + while (StartOf(2)) { + NodeStatement(tmpParent); +@@ -888,28 +953,41 @@ void Parser::MultiValue(QDomElement& par + Get(); + } + } +- QDomElement child; +- QDomNodeList list = tmpParent.childNodes(); +- QDomElement field = doc->createElement("field"); +- field.setAttribute("name", fieldName); +- int i = 0; ++// QDomElement child; ++ pugi::xml_node child; ++// QDomNodeList list = tmpParent.childNodes(); ++ std::vector list; ++ for (auto item : tmpParent.children()) { ++ list.push_back(item); ++ } ++// QDomElement field = doc->createElement("field"); ++ pugi::xml_node field = parent.append_child("field"); ++// field.setAttribute("name", fieldName); ++ field.append_attribute("name") = fieldName.c_str(); ++ unsigned int i = 0; + while(i < list.size()) + { +- child = list.at(i).toElement(); +- if (flag) +- field.appendChild(child.cloneNode()); +- else +- parent.appendChild(child.cloneNode()); ++ child = list.at(i);//.toElement(); ++ if (flag) { ++// field.appendChild(child.cloneNode()); ++ field.append_copy(child); ++ } else { ++// parent.appendChild(child.cloneNode()); ++ parent.append_copy(child); ++ } + i++; + } +- if (flag) +- parent.appendChild(field); +- ++ if (flag) { ++// parent.appendChild(field); ++ } else { ++ parent.remove_child(field); ++ } + } else SynErr(103); + Expect(23); ++ doc->remove_child(tmpParent); + } + +-void Parser::MultiNumber(QString& value) { ++void Parser::MultiNumber(std::string& value) { + if (la->kind == 2) { + Get(); + } else if (la->kind == 3) { +@@ -932,7 +1010,7 @@ void Parser::MultiNumber(QString& value) + } + } + +-void Parser::MultiString(QString& value) { ++void Parser::MultiString(std::string& value) { + Expect(4); + value.append(coco_string_create_char(t->val)); + if (la->kind == 37) { +@@ -947,7 +1025,7 @@ void Parser::MultiString(QString& value) + } + } + +-void Parser::MultiBool(QString& value) { ++void Parser::MultiBool(std::string& value) { + if (la->kind == 82) { + Get(); + } else if (la->kind == 84) { +@@ -1173,7 +1251,7 @@ void Errors::Warning(const wchar_t *s) { + } + + void Errors::Exception(const wchar_t* s) { +- wprintf(L"%ls", s); ++ wprintf(L"%ls", s); + exit(1); + } + */ +diff -rupN -x .git autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Parser.h meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Parser.h +--- autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Parser.h 2024-12-06 18:01:12.268963700 -0800 ++++ meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Parser.h 2024-12-06 18:48:17.891284300 -0800 +@@ -35,9 +35,10 @@ + #if !defined(VRML_PARSER_H__) + #define VRML_PARSER_H__ + +-#include ++#include "contrib/pugixml/src/pugixml.hpp" ++#include + #include +- ++#include + + #include "Scanner.h" + +@@ -48,7 +49,7 @@ class Errors { + public: + int count; // number of errors detected + wchar_t* stringError; +- ++ + Errors(); + ~Errors(); + void SynErr(int line, int col, int n); +@@ -90,14 +91,15 @@ public: + Token *t; // last recognized token + Token *la; // lookahead token + +-QDomDocument *doc; +- +- std::map defNode; +- +- std::set proto; +- +- std::set x3dNode; +- ++ pugi::xml_document doc_; ++ pugi::xml_document *doc = &doc_; // IrrXMLReader* createIrrXMLReader(const char* filename); ++ ++ std::map defNode; ++ ++ std::set proto; ++ ++ std::set x3dNode; ++ + void InitX3dNode() + { + x3dNode.insert("Arc2D"); x3dNode.insert("ArcClose2D"); x3dNode.insert("BallJoint"); +@@ -116,7 +118,7 @@ QDomDocument *doc; + x3dNode.insert("EspduTransform"); x3dNode.insert("ExplosionEmitter"); + x3dNode.insert("FillProperties"); x3dNode.insert("FloatVertexAttribute"); + x3dNode.insert("FogCoordinate"); x3dNode.insert(" GeneratedCubeMapTexture"); +- x3dNode.insert("GeoCoordinate"); x3dNode.insert("GeoElevationGrid"); x3dNode.insert("GeoLocation"); ++ x3dNode.insert("GeoCoordinate"); x3dNode.insert("GeoElevationGrid"); x3dNode.insert("GeoLocation"); + x3dNode.insert("GeoLOD"); x3dNode.insert("GeoMetadata"); x3dNode.insert("GeoOrigin"); + x3dNode.insert("GeoPositionInterpolator"); x3dNode.insert("GeoProximitySensor"); + x3dNode.insert("GeoTouchSensor"); x3dNode.insert("GeoViewpoint"); x3dNode.insert("GravityPhysicsModel"); +@@ -128,7 +130,7 @@ QDomDocument *doc; + x3dNode.insert("Layer"); x3dNode.insert("LayerSet"); x3dNode.insert("Layout"); + x3dNode.insert("LayoutGroup"); x3dNode.insert("LayoutLayer"); x3dNode.insert("LinePicker"); + x3dNode.insert("LineProperties"); x3dNode.insert("LineSet"); x3dNode.insert("LoadSensor"); +- x3dNode.insert("LocalFog"); x3dNode.insert("Material"); x3dNode.insert("Matrix3VertexAttribute"); ++ x3dNode.insert("LocalFog"); x3dNode.insert("Material"); x3dNode.insert("Matrix3VertexAttribute"); + x3dNode.insert("Matrix4VertexAttribute"); x3dNode.insert("MetadataDouble"); + x3dNode.insert("MetadataFloat"); x3dNode.insert("MetadataInteger"); x3dNode.insert("MetadataSet"); + x3dNode.insert("MetadataString"); x3dNode.insert("MotorJoint"); x3dNode.insert("MultiTexture"); +@@ -162,7 +164,7 @@ QDomDocument *doc; + x3dNode.insert(" Viewpoint"); x3dNode.insert("ViewpointGroup"); x3dNode.insert("VolumeEmitter"); + x3dNode.insert("VolumePicker"); x3dNode.insert("WindPhysicsModel"); x3dNode.insert("Cylinder"); x3dNode.insert("Sphere"); + } +- ++ + + + Parser(Scanner *scanner); +@@ -174,52 +176,52 @@ QDomDocument *doc; + void ProfileStatement(); + void ComponentStatements(); + void MetaStatements(); +- void Statements(QDomElement& parent); ++ void Statements(pugi::xml_node& parent); + void ProfileNameId(); + void ComponentStatement(); + void ComponentNameId(); + void ComponentSupportLevel(); + void ExportStatement(); +- void NodeNameId(QString& str); ++ void NodeNameId(std::string& str); + void ExportedNodeNameId(); + void ImportStatement(); + void InlineNodeNameId(); + void MetaStatement(); + void Metakey(); + void Metavalue(); +- void Statement(QDomElement& parent); +- void NodeStatement(QDomElement& parent); +- void ProtoStatement(QDomElement& parent); ++ void Statement(pugi::xml_node& parent); ++ void NodeStatement(pugi::xml_node& parent); ++ void ProtoStatement(pugi::xml_node& parent); + void RouteStatement(); +- void Node(QDomElement& parent, QString& tagName, const QString defValue); +- void RootNodeStatement(QDomElement& parent); +- void Proto(QDomElement& parent); +- void Externproto(QDomElement& parent); +- void ProtoStatements(QDomElement& parent); +- void NodeTypeId(QString& str); +- void InterfaceDeclarations(QDomElement& parent); +- void ProtoBody(QDomElement& parent); +- void InterfaceDeclaration(QDomElement& parent); +- void RestrictedInterfaceDeclaration(QDomElement& parent); +- void FieldType(QString& str); +- void InputOnlyId(QString& str); +- void OutputOnlyId(QString& str); +- void InitializeOnlyId(QString& str); +- void FieldValue(QDomElement& parent, QString fieldName, bool flag); +- void FieldId(QString& str); +- void ExternInterfaceDeclarations(QDomElement& parent); +- void URLList(QString& url); +- void ExternInterfaceDeclaration(QDomElement& parent); +- void NodeBody(QDomElement& parent, bool flag); ++ void Node(pugi::xml_node& parent, std::string& tagName, const std::string defValue); ++ void RootNodeStatement(pugi::xml_node& parent); ++ void Proto(pugi::xml_node& parent); ++ void Externproto(pugi::xml_node& parent); ++ void ProtoStatements(pugi::xml_node& parent); ++ void NodeTypeId(std::string& str); ++ void InterfaceDeclarations(pugi::xml_node& parent); ++ void ProtoBody(pugi::xml_node& parent); ++ void InterfaceDeclaration(pugi::xml_node& parent); ++ void RestrictedInterfaceDeclaration(pugi::xml_node& parent); ++ void FieldType(std::string& str); ++ void InputOnlyId(std::string& str); ++ void OutputOnlyId(std::string& str); ++ void InitializeOnlyId(std::string& str); ++ void FieldValue(pugi::xml_node& parent, std::string fieldName, bool flag); ++ void FieldId(std::string& str); ++ void ExternInterfaceDeclarations(pugi::xml_node& parent); ++ void URLList(std::string& url); ++ void ExternInterfaceDeclaration(pugi::xml_node& parent); ++ void NodeBody(pugi::xml_node& parent, bool flag); + void ScriptBody(); +- void NodeBodyElement(QDomElement& parent, bool flag); ++ void NodeBodyElement(pugi::xml_node& parent, bool flag); + void ScriptBodyElement(); +- void InputOutputId(QString& str); +- void SingleValue(QDomElement& parent, QString fieldName, bool flag); +- void MultiValue(QDomElement& parent, QString fieldName, bool flag); +- void MultiNumber(QString& value); +- void MultiString(QString& value); +- void MultiBool(QString& value); ++ void InputOutputId(std::string& str); ++ void SingleValue(pugi::xml_node& parent, std::string fieldName, bool flag); ++ void MultiValue(pugi::xml_node& parent, std::string fieldName, bool flag); ++ void MultiNumber(std::string& value); ++ void MultiString(std::string& value); ++ void MultiBool(std::string& value); + + void Parse(); + +diff -rupN -x .git autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Scanner.cpp meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Scanner.cpp +--- autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Scanner.cpp 2024-12-06 18:01:12.268963700 -0800 ++++ meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Scanner.cpp 2024-12-06 14:21:47.920443100 -0800 +@@ -39,7 +39,7 @@ + + wchar_t* coco_string_create(const wchar_t* value) { + wchar_t* data; +- int len = 0; ++ size_t len = 0; + if (value) { len = wcslen(value); } + data = new wchar_t[len + 1]; + wcsncpy(data, value, len); +@@ -63,7 +63,7 @@ wchar_t* coco_string_create_upper(const + if (!data) { return NULL; } + + int dataLen = 0; +- if (data) { dataLen = wcslen(data); } ++ if (data) { dataLen = static_cast(wcslen(data)); } + + wchar_t *newData = new wchar_t[dataLen + 1]; + +@@ -80,7 +80,7 @@ wchar_t* coco_string_create_upper(const + + wchar_t* coco_string_create_lower(const wchar_t* data) { + if (!data) { return NULL; } +- int dataLen = wcslen(data); ++ int dataLen = static_cast(wcslen(data)); + return coco_string_create_lower(data, 0, dataLen); + } + +@@ -102,11 +102,11 @@ wchar_t* coco_string_create_lower(const + + wchar_t* coco_string_create_append(const wchar_t* data1, const wchar_t* data2) { + wchar_t* data; +- int data1Len = 0; +- int data2Len = 0; ++ size_t data1Len = 0; ++ size_t data2Len = 0; + + if (data1) { data1Len = wcslen(data1); } +- if (data2) {data2Len = wcslen(data2); } ++ if (data2) { data2Len = wcslen(data2); } + + data = new wchar_t[data1Len + data2Len + 1]; + +@@ -133,13 +133,13 @@ void coco_string_delete(wchar_t* &data) + } + + int coco_string_length(const wchar_t* data) { +- if (data) { return wcslen(data); } ++ if (data) { return static_cast(wcslen(data)); } + return 0; + } + + bool coco_string_endswith(const wchar_t* data, const wchar_t *end) { +- int dataLen = wcslen(data); +- int endLen = wcslen(end); ++ size_t dataLen = wcslen(data); ++ size_t endLen = wcslen(end); + return (endLen <= dataLen) && (wcscmp(data + dataLen - endLen, end) == 0); + } + +@@ -186,8 +186,8 @@ int coco_string_hash(const wchar_t *data + // string handling, ascii character + + wchar_t* coco_string_create(const char* value) { +- int len = 0; +- if (value) { len = strlen(value); } ++ int len = 0; ++ if (value) { len = static_cast(strlen(value)); } + wchar_t* data = new wchar_t[len + 1]; + for (int i = 0; i < len; ++i) { data[i] = (wchar_t) value[i]; } + data[len] = 0; +@@ -240,7 +240,7 @@ Buffer::Buffer(FILE* s, bool isUserStrea + fileLen = bufLen = bufStart = 0; + } + bufCapacity = (bufLen>0) ? bufLen : MIN_BUFFER_LENGTH; +- buf = new unsigned char[bufCapacity]; ++ buf = new unsigned char[bufCapacity]; + if (fileLen > 0) SetPos(0); // setup buffer to position 0 (start) + else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid + if (bufLen == fileLen && CanSeek()) Close(); +@@ -270,7 +270,7 @@ Buffer::Buffer(const unsigned char* buf, + } + + Buffer::~Buffer() { +- Close(); ++ Close(); + if (buf != NULL) { + delete [] buf; + buf = NULL; +@@ -306,12 +306,12 @@ int Buffer::Peek() { + + wchar_t* Buffer::GetString(int beg, int end) { + int len = end - beg; +- wchar_t *buf = new wchar_t[len]; ++ wchar_t *retBuf = new wchar_t[len]; + int oldPos = GetPos(); + SetPos(beg); +- for (int i = 0; i < len; ++i) buf[i] = (wchar_t) Read(); ++ for (int i = 0; i < len; ++i) retBuf[i] = (wchar_t) Read(); + SetPos(oldPos); +- return buf; ++ return retBuf; + } + + int Buffer::GetPos() { +@@ -329,16 +329,16 @@ void Buffer::SetPos(int value) { + + if ((value < 0) || (value > fileLen)) { + char msg[50]; +- sprintf(msg, "Buffer out of bounds access, position: %d", value); ++ snprintf(msg, 50, "Buffer out of bounds access, position: %d", value); + throw msg; +- ++ + } + + if ((value >= bufStart) && (value < (bufStart + bufLen))) { // already in buffer + bufPos = value - bufStart; + } else if (stream != NULL) { // must be swapped in + fseek(stream, value, SEEK_SET); +- bufLen = fread(buf, sizeof(unsigned char), bufCapacity, stream); ++ bufLen = static_cast(fread(buf, sizeof(unsigned char), bufCapacity, stream)); + bufStart = value; bufPos = 0; + } else { + bufPos = fileLen - bufStart; // make Pos return fileLen +@@ -362,7 +362,7 @@ int Buffer::ReadNextStreamChunk() { + buf = newBuf; + free = bufLen; + } +- int read = fread(buf + bufLen, sizeof(unsigned char), free, stream); ++ int read = static_cast(fread(buf + bufLen, sizeof(unsigned char), free, stream)); + if (read > 0) { + fileLen = bufLen = (bufLen + read); + return read; +@@ -416,7 +416,7 @@ Scanner::Scanner(const wchar_t* fileName + char *chFileName = coco_string_create_char(fileName); + if ((stream = fopen(chFileName, "rb")) == NULL) { + char msg[50]; +- sprintf(msg, "Can not open file: %s", chFileName); ++ snprintf(msg, 50, "Can not open file: %s", chFileName); + coco_string_delete(chFileName); + throw msg; + } +@@ -554,7 +554,7 @@ void Scanner::Init() { + heapEnd = (void**) (((char*) heap) + HEAP_BLOCK_SIZE); + *heapEnd = 0; + heapTop = heap; +- if (sizeof(Token) > HEAP_BLOCK_SIZE) { ++ if constexpr (sizeof(Token) > HEAP_BLOCK_SIZE) { + throw "Too small HEAP_BLOCK_SIZE"; + } + +@@ -637,18 +637,18 @@ void Scanner::CreateHeapBlock() { + } + + Token* Scanner::CreateToken() { +- Token *t; ++ Token *tkn; + if (((char*) heapTop + (int) sizeof(Token)) >= (char*) heapEnd) { + CreateHeapBlock(); + } +- t = (Token*) heapTop; ++ tkn = (Token*) heapTop; + heapTop = (void*) ((char*) heapTop + sizeof(Token)); +- t->val = NULL; +- t->next = NULL; +- return t; ++ tkn->val = NULL; ++ tkn->next = NULL; ++ return tkn; + } + +-void Scanner::AppendVal(Token *t) { ++void Scanner::AppendVal(Token *tkn) { + int reqMem = (tlen + 1) * sizeof(wchar_t); + if (((char*) heapTop + reqMem) >= (char*) heapEnd) { + if (reqMem > HEAP_BLOCK_SIZE) { +@@ -656,11 +656,11 @@ void Scanner::AppendVal(Token *t) { + } + CreateHeapBlock(); + } +- t->val = (wchar_t*) heapTop; ++ tkn->val = (wchar_t*) heapTop; + heapTop = (void*) ((char*) heapTop + reqMem); + +- wcsncpy(t->val, tval, tlen); +- t->val[tlen] = L'\0'; ++ wcsncpy(tkn->val, tval, tlen); ++ tkn->val[tlen] = L'\0'; + } + + Token* Scanner::NextToken() { +diff -rupN -x .git autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Scanner.h meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Scanner.h +--- autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Scanner.h 2024-12-06 18:01:12.268963700 -0800 ++++ meshlab_repo_patch/src/meshlabplugins/io_x3d/vrml/Scanner.h 2024-12-06 14:21:59.453748300 -0800 +@@ -52,9 +52,9 @@ + //#define coco_swprintf _snwprintf + //#elif defined __GNUC__ + //#define coco_swprintf swprintf +-//#else ++//#else + //#error unknown compiler! +-//#endif ++//#endif + + #ifdef WIN32 + #ifndef __MINGW32__ +@@ -62,7 +62,7 @@ + #define coco_swprintf swprintf_s + #elif _MSC_VER >= 1300 + #define coco_swprintf _snwprintf +- #else ++ #else + #error unknown compiler! + #endif + #else +@@ -106,7 +106,7 @@ void coco_string_delete(char* &data); + namespace VrmlTranslator { + + +-class Token ++class Token + { + public: + int kind; // token kind +@@ -135,10 +135,10 @@ private: + int bufPos; // current position in buffer + FILE* stream; // input stream (seekable) + bool isUserStream; // was the stream opened by the user? +- ++ + int ReadNextStreamChunk(); + bool CanSeek(); // true if stream can seek otherwise false +- ++ + public: + static const int EoF = COCO_WCHAR_MAX + 1; + +@@ -146,7 +146,7 @@ public: + Buffer(const unsigned char* buf, int len); + Buffer(Buffer *b); + virtual ~Buffer(); +- ++ + virtual void Close(); + virtual int Read(); + virtual int Peek(); +@@ -256,7 +256,7 @@ private: + int eofSym; + int noSym; + int maxT; +- int charSetSize; ++// int charSetSize; // unused + StartStates start; + KeywordMap keywords; + +@@ -277,7 +277,7 @@ private: + + void CreateHeapBlock(); + Token* CreateToken(); +- void AppendVal(Token *t); ++ void AppendVal(Token *tkn); + + void Init(); + void NextCh(); +@@ -288,7 +288,7 @@ private: + + public: + Buffer *buffer; // scanner buffer +- ++ + Scanner(const unsigned char* buf, int len); + Scanner(const wchar_t* fileName); + Scanner(FILE* s); diff --git a/contrib/tinyusdz/README.md b/contrib/tinyusdz/README.md index b32feca6f..87b595993 100644 --- a/contrib/tinyusdz/README.md +++ b/contrib/tinyusdz/README.md @@ -5,7 +5,7 @@ tinyusdz repo is automatically cloned. Users who haven't opted-in to USD support won't be burdened with the extra download volume. -To update te git commit hash pulled down, modify `TINYUSDZ_GIT_TAG` in file +To update the git commit hash pulled down, modify `TINYUSDZ_GIT_TAG` in file `code/CMakeLists.txt` ## Notes diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 800d2e993..cd52127da 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "IOStream.hpp" #include +#include #include #include @@ -128,6 +129,11 @@ public: /// @return true, if the parsing was successful, false if not. bool parse(IOStream *stream); + /// @brief Will parse an xml-file from a stringstream. + /// @param[in] str The input istream (note: not "const" to match pugixml param) + /// @return true, if the parsing was successful, false if not. + bool parse(std::istream &inStream); + /// @brief Will return true if a root node is there. /// @return true in case of an existing root. bool hasRoot() const; @@ -311,7 +317,23 @@ bool TXmlParser::parse(IOStream *stream) { mDoc = new pugi::xml_document(); // load_string assumes native encoding (aka always utf-8 per build options) //pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); - pugi::xml_parse_result parse_result = mDoc->load_buffer(&mData[0], mData.size(), pugi::parse_full); + pugi::xml_parse_result parse_result = mDoc->load_buffer(&mData[0], mData.size(), pugi::parse_full); + if (parse_result.status == pugi::status_ok) { + return true; + } + + ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); + + return false; +} + +template +bool TXmlParser::parse(std::istream &inStream) { + if (hasRoot()) { + clear(); + } + mDoc = new pugi::xml_document(); + pugi::xml_parse_result parse_result = mDoc->load(inStream); if (parse_result.status == pugi::status_ok) { return true; } diff --git a/test/models/WRL/HelloWorld.wrl b/test/models/WRL/HelloWorld.wrl new file mode 100644 index 000000000..e6c71b6a5 --- /dev/null +++ b/test/models/WRL/HelloWorld.wrl @@ -0,0 +1,87 @@ +#VRML V2.0 utf8 +# X3D-to-VRML-97 XSL translation autogenerated by X3dToVrml97.xslt +# https://www.web3d.org/x3d/content/X3dToVrml97.xslt +# Generated using XSLT processor: Saxonica + +# [X3D] VRML V3.3 utf8 +# PROFILE Immersive +# [X3D] version=3.3 +# [X3D] noNamespaceSchemaLocation=https://www.web3d.org/specifications/x3d-3.3.xsd +# [head] + +# Alternate encodings: VRML97, X3D ClassicVRML Encoding, X3D Compressed Binary Encoding (CBE), X3DOM, JSON +# META "title" "HelloWorld.x3d" +# META "description" "Simple X3D model example: Hello World!" +# META "created" "30 October 2000" +# META "modified" "9 July 2023" +# META "creator" "Don Brutzman" +# META "Image" "HelloWorld.tall.png" +# META "reference" "https://en.wikipedia.org/wiki/Hello_world" +# META "reference" "https://en.wikipedia.org/wiki/Hello#.22Hello.2C_World.22_computer_program" +# META "reference" "https://en.wikipedia.org/wiki/\"Hello,_World!\"_program" +# META "reference" "https://en.wikibooks.org/w/index.php?title=Computer_Programming/Hello_world" +# META "reference" "https://www.HelloWorldExample.net" +# META "reference" "https://www.web3d.org" +# META "reference" "https://www.web3d.org/realtime-3d/news/internationalization-x3d" +# META "reference" "https://www.web3d.org/x3d/content/examples/HelloWorld.x3d" +# META "reference" "https://www.web3d.org/x3d/content/examples/X3dForAdvancedModeling/HelloWorldScenes/HelloWorld.x3d" +# META "identifier" "https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter01TechnicalOverview/HelloWorld.x3d" +# META "license" "https://www.web3d.org/x3d/content/examples/license.html" +# META "generator" "X3D-Edit 4.0, https://savage.nps.edu/X3D-Edit" +# META "reference" "HelloWorld.wrl" +# META "reference" "HelloWorld.x3dv" +# META "reference" "HelloWorld.x3db" +# META "reference" "HelloWorld.xhtml" +# META "reference" "HelloWorld.json" + +# [Scene] ========== ========== ========== + +NavigationInfo { type [ "EXAMINE" "ANY" ] } ### Default X3D NavigationInfo + +# Example scene to illustrate X3D nodes and fields (XML elements and attributes) +WorldInfo { + info [ "Example scene to illustrate a simple X3D model" ] + title "Hello World!" +} +Group { + children [ + DEF ViewUpClose Viewpoint { + centerOfRotation 0 -1 0 + description "Hello world!" + position 0 -1 7 + } + Transform { + rotation 0 1 0 3 + children [ + Shape { + geometry Sphere { + } + appearance Appearance { + material DEF MaterialOffWhite Material { + diffuseColor 0.980392 0.976471 0.964706 + } + texture DEF ImageCloudlessEarth ImageTexture { + url [ "earth-topo.png" "earth-topo.jpg" "earth-topo-small.gif" "https://www.web3d.org/x3d/content/examples/Basic/earth-topo.png" "https://www.web3d.org/x3d/content/examples/Basic/earth-topo.jpg" "https://www.web3d.org/x3d/content/examples/Basic/earth-topo-small.gif" ] + } + } + } + ] + } + Transform { + translation 0 -2 0 + children [ + Shape { + geometry DEF TextMessage Text { + string [ "Hello" "world!" ] + fontStyle FontStyle { + justify [ "MIDDLE" "MIDDLE" ] + } + } + appearance Appearance { + material USE MaterialOffWhite + } + } + ] + } + ] +} diff --git a/test/models/WRL/README.md b/test/models/WRL/README.md new file mode 100644 index 000000000..744d2485e --- /dev/null +++ b/test/models/WRL/README.md @@ -0,0 +1,6 @@ +# WRL models + +X3D is a modern superset of the old (VRML) WRL format + +# HelloWorld.wrl +Downloaded from [HelloWorld.wrl](http://www.web3d.org/x3d/content/examples/HelloWorld.wrl) diff --git a/test/models/WRL/earth-topo.png b/test/models/WRL/earth-topo.png new file mode 100644 index 000000000..1749cb835 Binary files /dev/null and b/test/models/WRL/earth-topo.png differ diff --git a/test/models/WRL/ref/README.md b/test/models/WRL/ref/README.md new file mode 100644 index 000000000..ff0205010 --- /dev/null +++ b/test/models/WRL/ref/README.md @@ -0,0 +1,4 @@ +# WRL 3D model reference images + +## HelloWorld.wrl +HelloWorld.wrl diff --git a/test/models/WRL/ref/screenshots/HelloWorld_wrl.png b/test/models/WRL/ref/screenshots/HelloWorld_wrl.png new file mode 100644 index 000000000..54cfd704c Binary files /dev/null and b/test/models/WRL/ref/screenshots/HelloWorld_wrl.png differ diff --git a/test/models/X3D/HelloWorld.x3d b/test/models/X3D/HelloWorld.x3d new file mode 100644 index 000000000..030e2f36c --- /dev/null +++ b/test/models/X3D/HelloWorld.x3d @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/models/X3D/README.md b/test/models/X3D/README.md new file mode 100644 index 000000000..dd843871e --- /dev/null +++ b/test/models/X3D/README.md @@ -0,0 +1,4 @@ +# X3D models + +# HelloWorld.x3d +Downloaded from [HelloWorld.x3d](http://www.web3d.org/x3d/content/examples/HelloWorld.x3d) diff --git a/test/models/X3D/earth-topo.png b/test/models/X3D/earth-topo.png new file mode 100644 index 000000000..1749cb835 Binary files /dev/null and b/test/models/X3D/earth-topo.png differ diff --git a/test/models/X3D/ref/README.md b/test/models/X3D/ref/README.md index dd6f48384..dc80e3064 100644 --- a/test/models/X3D/ref/README.md +++ b/test/models/X3D/ref/README.md @@ -12,3 +12,6 @@ ComputerKeyboard as of 1 Dec 2021 git commit `1614934`, using pugi xml parsing, missing meshes and obvious artifacts: ComputerKeyboard.x3d (pugi xml) + +## HelloWorld.x3d +HelloWorld.x3d diff --git a/test/models/X3D/ref/screenshots/HelloWorld_x3d.png b/test/models/X3D/ref/screenshots/HelloWorld_x3d.png new file mode 100644 index 000000000..581a2988d Binary files /dev/null and b/test/models/X3D/ref/screenshots/HelloWorld_x3d.png differ diff --git a/test/models/X3DB/HelloWorld.x3db b/test/models/X3DB/HelloWorld.x3db new file mode 100644 index 000000000..ba2f5c67c Binary files /dev/null and b/test/models/X3DB/HelloWorld.x3db differ diff --git a/test/models/X3DB/README.md b/test/models/X3DB/README.md new file mode 100644 index 000000000..9e83c8dd0 --- /dev/null +++ b/test/models/X3DB/README.md @@ -0,0 +1,6 @@ +# X3DB models + +X3DB models are compressed X3D models + +# HelloWorld.x3db +Downloaded from [HelloWorld.x3db](http://www.web3d.org/x3d/content/examples/HelloWorld.x3db) diff --git a/test/models/X3DB/earth-topo.png b/test/models/X3DB/earth-topo.png new file mode 100644 index 000000000..1749cb835 Binary files /dev/null and b/test/models/X3DB/earth-topo.png differ diff --git a/test/models/X3DV/HelloWorld.x3dv b/test/models/X3DV/HelloWorld.x3dv new file mode 100644 index 000000000..1da5c4fb5 --- /dev/null +++ b/test/models/X3DV/HelloWorld.x3dv @@ -0,0 +1,86 @@ +#X3D V3.3 utf8 +# X3D-to-ClassicVRML XSL translation autogenerated by X3dToVrml97.xslt +# https://www.web3d.org/x3d/content/X3dToVrml97.xslt +# Generated using XSLT processor: Saxonica + +PROFILE Immersive +# [X3D] version=3.3 +# [X3D] noNamespaceSchemaLocation=https://www.web3d.org/specifications/x3d-3.3.xsd +# [head] + +# Alternate encodings: VRML97, X3D ClassicVRML Encoding, X3D Compressed Binary Encoding (CBE), X3DOM, JSON +META "title" "HelloWorld.x3d" +META "description" "Simple X3D model example: Hello World!" +META "created" "30 October 2000" +META "modified" "9 July 2023" +META "creator" "Don Brutzman" +META "Image" "HelloWorld.tall.png" +META "reference" "https://en.wikipedia.org/wiki/Hello_world" +META "reference" "https://en.wikipedia.org/wiki/Hello#.22Hello.2C_World.22_computer_program" +META "reference" "https://en.wikipedia.org/wiki/\"Hello,_World!\"_program" +META "reference" "https://en.wikibooks.org/w/index.php?title=Computer_Programming/Hello_world" +META "reference" "https://www.HelloWorldExample.net" +META "reference" "https://www.web3d.org" +META "reference" "https://www.web3d.org/realtime-3d/news/internationalization-x3d" +META "reference" "https://www.web3d.org/x3d/content/examples/HelloWorld.x3d" +META "reference" "https://www.web3d.org/x3d/content/examples/X3dForAdvancedModeling/HelloWorldScenes/HelloWorld.x3d" +META "identifier" "https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter01TechnicalOverview/HelloWorld.x3d" +META "license" "https://www.web3d.org/x3d/content/examples/license.html" +META "generator" "X3D-Edit 4.0, https://savage.nps.edu/X3D-Edit" +META "reference" "HelloWorld.wrl" +META "reference" "HelloWorld.x3dv" +META "reference" "HelloWorld.x3db" +META "reference" "HelloWorld.xhtml" +META "reference" "HelloWorld.json" + +# [Scene] ========== ========== ========== + +NavigationInfo { type [ "EXAMINE" "ANY" ] } ### Default X3D NavigationInfo + +# Example scene to illustrate X3D nodes and fields (XML elements and attributes) +WorldInfo { + info [ "Example scene to illustrate a simple X3D model" ] + title "Hello World!" +} +Group { + children [ + DEF ViewUpClose Viewpoint { + centerOfRotation 0 -1 0 + description "Hello world!" + position 0 -1 7 + } + Transform { + rotation 0 1 0 3 + children [ + Shape { + geometry Sphere { + } + appearance Appearance { + material DEF MaterialOffWhite Material { + diffuseColor 0.980392 0.976471 0.964706 + } + texture DEF ImageCloudlessEarth ImageTexture { + url [ "earth-topo.png" "earth-topo.jpg" "earth-topo-small.gif" "https://www.web3d.org/x3d/content/examples/Basic/earth-topo.png" "https://www.web3d.org/x3d/content/examples/Basic/earth-topo.jpg" "https://www.web3d.org/x3d/content/examples/Basic/earth-topo-small.gif" ] + } + } + } + ] + } + Transform { + translation 0 -2 0 + children [ + Shape { + geometry DEF TextMessage Text { + string [ "Hello" "world!" ] + fontStyle FontStyle { + justify [ "MIDDLE" "MIDDLE" ] + } + } + appearance Appearance { + material USE MaterialOffWhite + } + } + ] + } + ] +} diff --git a/test/models/X3DV/README.md b/test/models/X3DV/README.md new file mode 100644 index 000000000..83c951c6a --- /dev/null +++ b/test/models/X3DV/README.md @@ -0,0 +1,6 @@ +# X3DV models + +X3DV models are as simple WRL (VRML) models with the version number upgraded from `2` to `3` + +# HelloWorld.x3dv +Downloaded from [HelloWorld.x3dv](http://www.web3d.org/x3d/content/examples/HelloWorld.x3dv) diff --git a/test/models/X3DV/earth-topo.png b/test/models/X3DV/earth-topo.png new file mode 100644 index 000000000..1749cb835 Binary files /dev/null and b/test/models/X3DV/earth-topo.png differ diff --git a/test/models/X3DV/ref/README.md b/test/models/X3DV/ref/README.md new file mode 100644 index 000000000..25f3e93d9 --- /dev/null +++ b/test/models/X3DV/ref/README.md @@ -0,0 +1,4 @@ +# X3DV 3D model reference images + +## HelloWorld.x3dv +HelloWorld.x3dv diff --git a/test/models/X3DV/ref/screenshots/HelloWorld_x3dv.png b/test/models/X3DV/ref/screenshots/HelloWorld_x3dv.png new file mode 100644 index 000000000..0860c7a85 Binary files /dev/null and b/test/models/X3DV/ref/screenshots/HelloWorld_x3dv.png differ