Skip to content

Commit 69558d8

Browse files
tellypresenceSteve Mkimkulling
authored
Introduce VRML format (.wrl and .x3dv) 3D model support (assimp#5857)
- Introduce VRML format (.wrl and .x3dv) 3D model support - Add samples --------- Co-authored-by: Steve M <[email protected]> Co-authored-by: Kim Kulling <[email protected]>
1 parent 5e09157 commit 69558d8

31 files changed

+1826
-24
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,7 @@ tools/assimp_qt_viewer/ui_mainwindow.h
122122
generated/*
123123

124124
# 3rd party cloned repos/tarballs etc
125+
# meshlab repo, automatically cloned via CMake (to gain 2 source files for VRML file format conversion)
126+
contrib/meshlab/autoclone
125127
# tinyusdz repo, automatically cloned via CMake
126128
contrib/tinyusdz/autoclone

CMakeLists.txt

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,47 @@ SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW)
4040

4141
CMAKE_MINIMUM_REQUIRED( VERSION 3.22 )
4242

43+
#================================================================================#
44+
# Model formats not enabled by default
45+
#
46+
# 3rd party projects may not adhere to strict standards enforced by assimp,
47+
# in which case those formats must be opt-in; otherwise the 3rd party code
48+
# would fail assimp CI checks
49+
#================================================================================#
50+
# M3D format import support (assimp integration no longer supported by M3D format author)
51+
# User may override these in their CMake script to provide M3D import/export support
52+
# (M3D importer/exporter was disabled for assimp release 5.1 or later)
53+
option(ASSIMP_BUILD_M3D_IMPORTER "Enable M3D file import" off)
54+
option(ASSIMP_BUILD_M3D_EXPORTER "Enable M3D file export" off)
55+
4356
# Experimental USD importer: disabled, need to opt-in
4457
# Note: assimp github PR automatic checks will fail the PR due to compiler warnings in
4558
# the external, 3rd party tinyusdz code which isn't technically part of the PR since it's
4659
# auto-cloned during build; so MUST disable the feature or the PR will be rejected
4760
option(ASSIMP_BUILD_USD_IMPORTER "Enable USD file import" off)
4861
option(ASSIMP_BUILD_USD_VERBOSE_LOGS "Enable verbose USD import debug logging" off)
62+
63+
# VRML (.wrl/.x3dv) file import support by leveraging X3D importer and 3rd party file
64+
# format converter to convert .wrl/.x3dv files to X3D-compatible .xml
65+
# (Need to make this opt-in because 3rd party code triggers lots of CI code quality warnings)
66+
option(ASSIMP_BUILD_VRML_IMPORTER "Enable VRML (.wrl/.x3dv) file import" off)
67+
#--------------------------------------------------------------------------------#
68+
# Internal impl for optional model formats
69+
#--------------------------------------------------------------------------------#
70+
# Internal/private M3D logic
71+
if (NOT ASSIMP_BUILD_M3D_IMPORTER)
72+
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER)
73+
endif () # if (not ASSIMP_BUILD_M3D_IMPORTER)
74+
if (NOT ASSIMP_BUILD_M3D_EXPORTER)
75+
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER)
76+
endif () # if (not ASSIMP_BUILD_M3D_EXPORTER)
77+
78+
# Internal/private VRML logic
79+
if (NOT ASSIMP_BUILD_VRML_IMPORTER)
80+
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_VRML_IMPORTER)
81+
endif () # if (not ASSIMP_BUILD_VRML_IMPORTER)
82+
#================================================================================#
83+
4984
option(ASSIMP_BUILD_USE_CCACHE "Use ccache to speed up compilation." on)
5085

5186
if(ASSIMP_BUILD_USE_CCACHE)
@@ -56,19 +91,6 @@ if(ASSIMP_BUILD_USE_CCACHE)
5691
endif()
5792
endif()
5893

59-
# User may override these in their CMake script to provide M3D import/export support
60-
# (M3D importer/exporter was disabled for assimp release 5.1 or later)
61-
option(ASSIMP_BUILD_M3D_IMPORTER "Enable M3D file import" off)
62-
option(ASSIMP_BUILD_M3D_EXPORTER "Enable M3D file export" off)
63-
64-
# Internal/private M3D logic
65-
if (NOT ASSIMP_BUILD_M3D_IMPORTER)
66-
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER)
67-
endif () # if (not ASSIMP_BUILD_M3D_IMPORTER)
68-
if (NOT ASSIMP_BUILD_M3D_EXPORTER)
69-
ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER)
70-
endif () # if (not ASSIMP_BUILD_M3D_EXPORTER)
71-
7294
# Toggles the use of the hunter package manager
7395
option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
7496

code/AssetLib/VRML/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# WRL/X3DV to X3D file format converter
2+
3+
## VRML and X3D 3D model formats background
4+
"VRML" 3D model files use either `VRML97` (`.wrl`) or "Classic VRML" (`.x3dv`)
5+
file formats.
6+
7+
The X3D model specification was introduced after these formats, as a superset of both WRL and X3DV.
8+
While X3D can understand the _content_ of WRL/X3DV files, it can't directly parse them because
9+
X3D uses `.xml` files, rather than `VRML97` or "Classic VRML" format.
10+
11+
But, if a converter is available to migrate just the file format (preserving the content), so that
12+
the `.wrl`/`.x3dv` files can be converted to an X3D-compatible `.xml` file, then the X3D importer
13+
will be able to load the resulting model file.
14+
15+
## How this code is used
16+
The sole purpose of `Parser`/`Scanner` (adopted from the `meshlab` project) is to take a
17+
`VRML97` (`.wrl`) or "Classic VRML" (`.x3dv`) file as input, and convert to an X3D `.xml` file.
18+
That's it.
19+
20+
By passing the converted in-memory `.xml` file content to the `X3DImporter`, the `.wrl` or `x3dv`
21+
model can be loaded via assimp.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Open Asset Import Library (assimp)
3+
----------------------------------------------------------------------
4+
5+
Copyright (c) 2006-2024, assimp team
6+
7+
All rights reserved.
8+
9+
Redistribution and use of this software in source and binary forms,
10+
with or without modification, are permitted provided that the
11+
following conditions are met:
12+
13+
* Redistributions of source code must retain the above
14+
copyright notice, this list of conditions and the
15+
following disclaimer.
16+
17+
* Redistributions in binary form must reproduce the above
18+
copyright notice, this list of conditions and the
19+
following disclaimer in the documentation and/or other
20+
materials provided with the distribution.
21+
22+
* Neither the name of the assimp team, nor the names of its
23+
contributors may be used to endorse or promote products
24+
derived from this software without specific prior
25+
written permission of the assimp team.
26+
27+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38+
39+
----------------------------------------------------------------------
40+
*/
41+
/// \file VrmlImporter.cpp
42+
/// \brief Convert VRML-formatted (.wrl, .x3dv) files to X3D .xml format
43+
/// \date 2024
44+
/// \author tellypresence
45+
46+
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
47+
48+
#include <memory> // std::unique_ptr
49+
#include "VrmlConverter.hpp"
50+
51+
namespace Assimp {
52+
53+
bool isFileWrlVrml97Ext(const std::string &pFile) {
54+
size_t pos = pFile.find_last_of('.');
55+
if (pos == std::string::npos) {
56+
return false;
57+
}
58+
std::string ext = pFile.substr(pos + 1);
59+
if (ext.size() != 3) {
60+
return false;
61+
}
62+
return (ext[0] == 'w' || ext[0] == 'W') && (ext[1] == 'r' || ext[1] == 'R') && (ext[2] == 'l' || ext[2] == 'L');
63+
}
64+
65+
bool isFileX3dvClassicVrmlExt(const std::string &pFile) {
66+
size_t pos = pFile.find_last_of('.');
67+
if (pos == std::string::npos) {
68+
return false;
69+
}
70+
std::string ext = pFile.substr(pos + 1);
71+
if (ext.size() != 4) {
72+
return false;
73+
}
74+
return (ext[0] == 'x' || ext[0] == 'X') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D') && (ext[3] == 'v' || ext[3] == 'V');
75+
}
76+
77+
#if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
78+
static VrmlTranslator::Scanner createScanner(const std::string &pFile) {
79+
std::unique_ptr<wchar_t[]> wide_stringPtr{ new wchar_t[ pFile.length() + 1 ] };
80+
std::copy(pFile.begin(), pFile.end(), wide_stringPtr.get());
81+
wide_stringPtr[ pFile.length() ] = 0;
82+
83+
return VrmlTranslator::Scanner(wide_stringPtr.get());
84+
} // wide_stringPtr auto-deleted when leaving scope
85+
#endif // #if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
86+
87+
std::stringstream ConvertVrmlFileToX3dXmlFile(const std::string &pFile) {
88+
std::stringstream ss;
89+
if (isFileWrlVrml97Ext(pFile) || isFileX3dvClassicVrmlExt(pFile)) {
90+
#if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
91+
VrmlTranslator::Scanner scanner = createScanner(pFile);
92+
VrmlTranslator::Parser parser(&scanner);
93+
parser.Parse();
94+
ss.str("");
95+
parser.doc_.save(ss);
96+
#endif // #if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
97+
}
98+
return ss;
99+
}
100+
101+
} // namespace Assimp
102+
103+
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
Open Asset Import Library (assimp)
3+
----------------------------------------------------------------------
4+
5+
Copyright (c) 2006-2024, assimp team
6+
7+
All rights reserved.
8+
9+
Redistribution and use of this software in source and binary forms,
10+
with or without modification, are permitted provided that the
11+
following conditions are met:
12+
13+
* Redistributions of source code must retain the above
14+
copyright notice, this list of conditions and the
15+
following disclaimer.
16+
17+
* Redistributions in binary form must reproduce the above
18+
copyright notice, this list of conditions and the
19+
following disclaimer in the documentation and/or other
20+
materials provided with the distribution.
21+
22+
* Neither the name of the assimp team, nor the names of its
23+
contributors may be used to endorse or promote products
24+
derived from this software without specific prior
25+
written permission of the assimp team.
26+
27+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38+
39+
----------------------------------------------------------------------
40+
*/
41+
42+
#pragma once
43+
44+
#include <sstream>
45+
#include <string>
46+
47+
#if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
48+
#include "contrib/meshlab/autoclone/meshlab_repo-src/src/meshlabplugins/io_x3d/vrml/Parser.h"
49+
#endif // #if !defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
50+
51+
namespace Assimp {
52+
53+
bool isFileWrlVrml97Ext(const std::string &pFile);
54+
bool isFileX3dvClassicVrmlExt(const std::string &pFile);
55+
56+
std::stringstream ConvertVrmlFileToX3dXmlFile(const std::string &pFile);
57+
} // namespace Assimp

code/AssetLib/X3D/X3DImporter.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4545

4646
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
4747

48+
#include "AssetLib/VRML/VrmlConverter.hpp"
4849
#include "X3DImporter.hpp"
4950
#include "X3DImporter_Macro.hpp"
5051

@@ -54,11 +55,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5455
#include <iterator>
5556
#include <memory>
5657

58+
#if defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
59+
#define X3D_FORMATS_DESCR_STR "Extensible 3D(X3D, X3DB) Importer"
60+
#define X3D_FORMATS_EXTENSIONS_STR "x3d x3db"
61+
#else
62+
#define X3D_FORMATS_DESCR_STR "VRML(WRL, X3DV) and Extensible 3D(X3D, X3DB) Importer"
63+
#define X3D_FORMATS_EXTENSIONS_STR "wrl x3d x3db x3dv"
64+
#endif // #if defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
65+
5766
namespace Assimp {
5867

5968
/// Constant which holds the importer description
6069
const aiImporterDesc X3DImporter::Description = {
61-
"Extensible 3D(X3D) Importer",
70+
X3D_FORMATS_DESCR_STR,
6271
"smalcom",
6372
"",
6473
"See documentation in source code. Chapter: Limitations.",
@@ -67,7 +76,7 @@ const aiImporterDesc X3DImporter::Description = {
6776
0,
6877
0,
6978
0,
70-
"x3d x3db"
79+
X3D_FORMATS_EXTENSIONS_STR
7180
};
7281

7382
bool X3DImporter::isNodeEmpty(XmlNode &node) {
@@ -215,7 +224,19 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) {
215224
if (!theParser.parse(fileStream.get())) {
216225
return;
217226
}
227+
ParseFile(theParser);
228+
}
229+
230+
void X3DImporter::ParseFile(std::istream &myIstream) {
231+
XmlParser theParser;
232+
if (!theParser.parse(myIstream)) {
233+
LogInfo("ParseFile(): ERROR: failed to convert VRML istream to xml");
234+
return;
235+
}
236+
ParseFile(theParser);
237+
}
218238

239+
void X3DImporter::ParseFile(XmlParser &theParser) {
219240
XmlNode *node = theParser.findNode("X3D");
220241
if (nullptr == node) {
221242
return;
@@ -246,9 +267,13 @@ void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
246267
mpIOHandler = pIOHandler;
247268

248269
Clear();
249-
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
250-
if (!stream) {
251-
throw DeadlyImportError("Could not open file for reading");
270+
std::stringstream ss = ConvertVrmlFileToX3dXmlFile(pFile);
271+
const bool isReadFromMem{ ss.str().length() > 0 };
272+
if (!isReadFromMem) {
273+
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
274+
if (!stream) {
275+
throw DeadlyImportError("Could not open file for reading");
276+
}
252277
}
253278
std::string::size_type slashPos = pFile.find_last_of("\\/");
254279

@@ -257,9 +282,13 @@ void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
257282
pScene->mRootNode->mParent = nullptr;
258283
pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
259284

260-
pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
261-
ParseFile(pFile, pIOHandler);
262-
pIOHandler->PopDirectory();
285+
if (isReadFromMem) {
286+
ParseFile(ss);
287+
} else {
288+
pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
289+
ParseFile(pFile, pIOHandler);
290+
pIOHandler->PopDirectory();
291+
}
263292

264293
//search for root node element
265294

code/AssetLib/X3D/X3DImporter.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,9 @@ class X3DImporter : public BaseImporter {
275275
/// Also exception can be thrown if trouble will found.
276276
/// \param [in] pFile - name of file to be parsed.
277277
/// \param [in] pIOHandler - pointer to IO helper object.
278-
void ParseFile(const std::string &pFile, IOSystem *pIOHandler);
278+
void ParseFile(const std::string &file, IOSystem *pIOHandler);
279+
void ParseFile(std::istream &myIstream);
280+
void ParseFile(XmlParser &theParser);
279281
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const;
280282
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
281283
const aiImporterDesc *GetInfo() const;

0 commit comments

Comments
 (0)