Allow schema checking of glTF2 file.
parent
babf3b8e3d
commit
a283a255a5
|
@ -74,6 +74,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
#include <rapidjson/error/en.h>
|
#include <rapidjson/error/en.h>
|
||||||
#include <rapidjson/rapidjson.h>
|
#include <rapidjson/rapidjson.h>
|
||||||
|
#include <rapidjson/schema.h>
|
||||||
|
|
||||||
#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0)
|
#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0)
|
||||||
# pragma GCC diagnostic pop
|
# pragma GCC diagnostic pop
|
||||||
|
@ -1066,6 +1067,7 @@ class Asset {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IOSystem *mIOSystem;
|
IOSystem *mIOSystem;
|
||||||
|
rapidjson::IRemoteSchemaDocumentProvider *mSchemaDocumentProvider;
|
||||||
|
|
||||||
std::string mCurrentAssetDir;
|
std::string mCurrentAssetDir;
|
||||||
|
|
||||||
|
@ -1125,8 +1127,9 @@ public:
|
||||||
Ref<Scene> scene;
|
Ref<Scene> scene;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Asset(IOSystem *io = nullptr) :
|
Asset(IOSystem *io = nullptr, rapidjson::IRemoteSchemaDocumentProvider *schemaDocumentProvider = nullptr) :
|
||||||
mIOSystem(io),
|
mIOSystem(io),
|
||||||
|
mSchemaDocumentProvider(schemaDocumentProvider),
|
||||||
asset(),
|
asset(),
|
||||||
accessors(*this, "accessors"),
|
accessors(*this, "accessors"),
|
||||||
animations(*this, "animations"),
|
animations(*this, "animations"),
|
||||||
|
@ -1149,6 +1152,9 @@ public:
|
||||||
//! Main function
|
//! Main function
|
||||||
void Load(const std::string &file, bool isBinary = false);
|
void Load(const std::string &file, bool isBinary = false);
|
||||||
|
|
||||||
|
//! Parse the AssetMetadata and check that the version is 2.
|
||||||
|
bool CanRead(const std::string &pFile, bool isBinary = false);
|
||||||
|
|
||||||
//! Enables binary encoding on the asset
|
//! Enables binary encoding on the asset
|
||||||
void SetAsBinary();
|
void SetAsBinary();
|
||||||
|
|
||||||
|
@ -1160,6 +1166,11 @@ public:
|
||||||
private:
|
private:
|
||||||
void ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneData);
|
void ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneData);
|
||||||
|
|
||||||
|
//! Obtain a JSON document from the stream.
|
||||||
|
// \param second argument is a buffer used by the document. It must be kept
|
||||||
|
// alive while the document is in use.
|
||||||
|
Document ReadDocument(IOStream& stream, bool isBinary, std::vector<char>& sceneData);
|
||||||
|
|
||||||
void ReadExtensionsUsed(Document &doc);
|
void ReadExtensionsUsed(Document &doc);
|
||||||
void ReadExtensionsRequired(Document &doc);
|
void ReadExtensionsRequired(Document &doc);
|
||||||
|
|
||||||
|
|
|
@ -1777,28 +1777,16 @@ inline void Asset::ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneDa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Asset::Load(const std::string &pFile, bool isBinary) {
|
inline rapidjson::Document Asset::ReadDocument(IOStream &stream, bool isBinary, std::vector<char> &sceneData)
|
||||||
|
{
|
||||||
ASSIMP_LOG_DEBUG("Loading GLTF2 asset");
|
ASSIMP_LOG_DEBUG("Loading GLTF2 asset");
|
||||||
mCurrentAssetDir.clear();
|
|
||||||
/*int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
|
|
||||||
if (pos != int(std::string::npos)) */
|
|
||||||
|
|
||||||
if (0 != strncmp(pFile.c_str(), AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) {
|
|
||||||
mCurrentAssetDir = glTFCommon::getCurrentAssetDir(pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
|
|
||||||
if (!stream) {
|
|
||||||
throw DeadlyImportError("GLTF: Could not open file for reading");
|
|
||||||
}
|
|
||||||
|
|
||||||
// is binary? then read the header
|
// is binary? then read the header
|
||||||
std::vector<char> sceneData;
|
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
SetAsBinary(); // also creates the body buffer
|
SetAsBinary(); // also creates the body buffer
|
||||||
ReadBinaryHeader(*stream, sceneData);
|
ReadBinaryHeader(stream, sceneData);
|
||||||
} else {
|
} else {
|
||||||
mSceneLength = stream->FileSize();
|
mSceneLength = stream.FileSize();
|
||||||
mBodyLength = 0;
|
mBodyLength = 0;
|
||||||
|
|
||||||
// read the scene data
|
// read the scene data
|
||||||
|
@ -1806,7 +1794,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
|
||||||
sceneData.resize(mSceneLength + 1);
|
sceneData.resize(mSceneLength + 1);
|
||||||
sceneData[mSceneLength] = '\0';
|
sceneData[mSceneLength] = '\0';
|
||||||
|
|
||||||
if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
|
if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
|
||||||
throw DeadlyImportError("GLTF: Could not read the file contents");
|
throw DeadlyImportError("GLTF: Could not read the file contents");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1826,6 +1814,42 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
|
||||||
throw DeadlyImportError("GLTF: JSON document root must be a JSON object");
|
throw DeadlyImportError("GLTF: JSON document root must be a JSON object");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Asset::Load(const std::string &pFile, bool isBinary)
|
||||||
|
{
|
||||||
|
mCurrentAssetDir.clear();
|
||||||
|
/*int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
|
||||||
|
if (pos != int(std::string::npos)) */
|
||||||
|
|
||||||
|
if (0 != strncmp(pFile.c_str(), AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) {
|
||||||
|
mCurrentAssetDir = glTFCommon::getCurrentAssetDir(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
|
||||||
|
if (!stream) {
|
||||||
|
throw DeadlyImportError("GLTF: Could not open file for reading");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> sceneData;
|
||||||
|
rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData);
|
||||||
|
|
||||||
|
// If a schemaDocumentProvider is available, see if the glTF schema is present.
|
||||||
|
// If so, use it to validate the document.
|
||||||
|
if (mSchemaDocumentProvider) {
|
||||||
|
if (const rapidjson::SchemaDocument *gltfSchema = mSchemaDocumentProvider->GetRemoteDocument("glTF.schema.json", 16)) {
|
||||||
|
rapidjson::SchemaValidator validator(*gltfSchema);
|
||||||
|
if (!doc.Accept(validator)) {
|
||||||
|
rapidjson::StringBuffer pathBuffer;
|
||||||
|
validator.GetInvalidSchemaPointer().StringifyUriFragment(pathBuffer);
|
||||||
|
rapidjson::StringBuffer argumentBuffer;
|
||||||
|
validator.GetInvalidDocumentPointer().StringifyUriFragment(argumentBuffer);
|
||||||
|
throw DeadlyImportError("GLTF: The JSON document did not satisfy the glTF2 schema. Schema keyword: ", validator.GetInvalidSchemaKeyword(), ", document path: ", pathBuffer.GetString(), ", argument: ", argumentBuffer.GetString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fill the buffer instance for the current file embedded contents
|
// Fill the buffer instance for the current file embedded contents
|
||||||
if (mBodyLength > 0) {
|
if (mBodyLength > 0) {
|
||||||
if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) {
|
if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) {
|
||||||
|
@ -1882,6 +1906,25 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool Asset::CanRead(const std::string &pFile, bool isBinary)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
|
||||||
|
if (!stream) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::vector<char> sceneData;
|
||||||
|
rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData);
|
||||||
|
asset.Read(doc);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
inline void Asset::SetAsBinary() {
|
inline void Asset::SetAsBinary() {
|
||||||
if (!mBodyBuffer) {
|
if (!mBodyBuffer) {
|
||||||
mBodyBuffer = buffers.Create("binary_glTF");
|
mBodyBuffer = buffers.Create("binary_glTF");
|
||||||
|
|
|
@ -111,19 +111,16 @@ const aiImporterDesc *glTF2Importer::GetInfo() const {
|
||||||
return &desc;
|
return &desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool glTF2Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
|
bool glTF2Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig ) const {
|
||||||
const std::string &extension = GetExtension(pFile);
|
const std::string &extension = GetExtension(pFile);
|
||||||
|
|
||||||
if (extension != "gltf" && extension != "glb") {
|
if (!checkSig && (extension != "gltf") && (extension != "glb"))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (pIOHandler) {
|
if (pIOHandler) {
|
||||||
glTF2::Asset asset(pIOHandler);
|
glTF2::Asset asset(pIOHandler);
|
||||||
asset.Load(pFile, extension == "glb");
|
return asset.CanRead(pFile, extension == "glb");
|
||||||
std::string version = asset.asset.version;
|
}
|
||||||
return !version.empty() && version[0] == '2';
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1587,7 +1584,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
|
||||||
this->mScene = pScene;
|
this->mScene = pScene;
|
||||||
|
|
||||||
// read the asset file
|
// read the asset file
|
||||||
glTF2::Asset asset(pIOHandler);
|
glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(mSchemaDocumentProvider));
|
||||||
asset.Load(pFile, GetExtension(pFile) == "glb");
|
asset.Load(pFile, GetExtension(pFile) == "glb");
|
||||||
if (asset.scene) {
|
if (asset.scene) {
|
||||||
pScene->mName = asset.scene->name;
|
pScene->mName = asset.scene->name;
|
||||||
|
@ -1613,4 +1610,8 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void glTF2Importer::SetupProperties(const Importer *pImp) {
|
||||||
|
mSchemaDocumentProvider = static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(pImp->GetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER));
|
||||||
|
}
|
||||||
|
|
||||||
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
|
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
|
||||||
|
|
|
@ -65,6 +65,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
const aiImporterDesc *GetInfo() const override;
|
const aiImporterDesc *GetInfo() const override;
|
||||||
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
|
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
|
||||||
|
virtual void SetupProperties(const Importer *pImp) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ImportEmbeddedTextures(glTF2::Asset &a);
|
void ImportEmbeddedTextures(glTF2::Asset &a);
|
||||||
|
@ -80,6 +81,9 @@ private:
|
||||||
std::vector<unsigned int> meshOffsets;
|
std::vector<unsigned int> meshOffsets;
|
||||||
std::vector<int> embeddedTexIdxs;
|
std::vector<int> embeddedTexIdxs;
|
||||||
aiScene *mScene;
|
aiScene *mScene;
|
||||||
|
|
||||||
|
/// An instance of rapidjson::IRemoteSchemaDocumentProvider
|
||||||
|
void *mSchemaDocumentProvider = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Assimp
|
} // namespace Assimp
|
||||||
|
|
|
@ -1072,6 +1072,18 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Set a configuration property
|
||||||
|
bool Importer::SetPropertyPointer(const char* szName, void* value) {
|
||||||
|
ai_assert(nullptr != pimpl);
|
||||||
|
|
||||||
|
bool existing;
|
||||||
|
ASSIMP_BEGIN_EXCEPTION_REGION();
|
||||||
|
existing = SetGenericProperty<void*>(pimpl->mPointerProperties, szName,value);
|
||||||
|
ASSIMP_END_EXCEPTION_REGION(bool);
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Get a configuration property
|
// Get a configuration property
|
||||||
int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
|
int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
|
||||||
|
@ -1104,6 +1116,14 @@ aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& i
|
||||||
return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
|
return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Get a configuration property
|
||||||
|
void* Importer::GetPropertyPointer(const char* szName, void* iErrorReturn /*= nullptr*/) const {
|
||||||
|
ai_assert(nullptr != pimpl);
|
||||||
|
|
||||||
|
return GetGenericProperty<void*>(pimpl->mPointerProperties,szName,iErrorReturn);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Get the memory requirements of a single node
|
// Get the memory requirements of a single node
|
||||||
inline
|
inline
|
||||||
|
|
|
@ -73,12 +73,12 @@ public:
|
||||||
// Data type to store the key hash
|
// Data type to store the key hash
|
||||||
typedef unsigned int KeyType;
|
typedef unsigned int KeyType;
|
||||||
|
|
||||||
// typedefs for our four configuration maps.
|
// typedefs for our configuration maps.
|
||||||
// We don't need more, so there is no need for a generic solution
|
|
||||||
typedef std::map<KeyType, int> IntPropertyMap;
|
typedef std::map<KeyType, int> IntPropertyMap;
|
||||||
typedef std::map<KeyType, ai_real> FloatPropertyMap;
|
typedef std::map<KeyType, ai_real> FloatPropertyMap;
|
||||||
typedef std::map<KeyType, std::string> StringPropertyMap;
|
typedef std::map<KeyType, std::string> StringPropertyMap;
|
||||||
typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
|
typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
|
||||||
|
typedef std::map<KeyType, void*> PointerPropertyMap;
|
||||||
|
|
||||||
/** IO handler to use for all file accesses. */
|
/** IO handler to use for all file accesses. */
|
||||||
IOSystem* mIOHandler;
|
IOSystem* mIOHandler;
|
||||||
|
@ -116,6 +116,9 @@ public:
|
||||||
/** List of Matrix properties */
|
/** List of Matrix properties */
|
||||||
MatrixPropertyMap mMatrixProperties;
|
MatrixPropertyMap mMatrixProperties;
|
||||||
|
|
||||||
|
/** List of pointer properties */
|
||||||
|
PointerPropertyMap mPointerProperties;
|
||||||
|
|
||||||
/** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
|
/** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
|
||||||
* to be executed before and after every single post-process step */
|
* to be executed before and after every single post-process step */
|
||||||
bool bExtraVerbose;
|
bool bExtraVerbose;
|
||||||
|
@ -142,6 +145,7 @@ ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT :
|
||||||
mFloatProperties(),
|
mFloatProperties(),
|
||||||
mStringProperties(),
|
mStringProperties(),
|
||||||
mMatrixProperties(),
|
mMatrixProperties(),
|
||||||
|
mPointerProperties(),
|
||||||
bExtraVerbose( false ),
|
bExtraVerbose( false ),
|
||||||
mPPShared( nullptr ) {
|
mPPShared( nullptr ) {
|
||||||
// empty
|
// empty
|
||||||
|
|
|
@ -245,6 +245,12 @@ public:
|
||||||
*/
|
*/
|
||||||
bool SetPropertyMatrix(const char *szName, const aiMatrix4x4 &sValue);
|
bool SetPropertyMatrix(const char *szName, const aiMatrix4x4 &sValue);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Set a pointer configuration property.
|
||||||
|
* @see SetPropertyInteger()
|
||||||
|
*/
|
||||||
|
bool SetPropertyPointer(const char *szName, void *sValue);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Get a configuration property.
|
/** Get a configuration property.
|
||||||
* @param szName Name of the property. All supported properties
|
* @param szName Name of the property. All supported properties
|
||||||
|
@ -297,6 +303,15 @@ public:
|
||||||
aiMatrix4x4 GetPropertyMatrix(const char *szName,
|
aiMatrix4x4 GetPropertyMatrix(const char *szName,
|
||||||
const aiMatrix4x4 &sErrorReturn = aiMatrix4x4()) const;
|
const aiMatrix4x4 &sErrorReturn = aiMatrix4x4()) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Get a pointer configuration property
|
||||||
|
*
|
||||||
|
* The return value remains valid until the property is modified.
|
||||||
|
* @see GetPropertyInteger()
|
||||||
|
*/
|
||||||
|
void* GetPropertyPointer(const char *szName,
|
||||||
|
void *sErrorReturn = nullptr) const;
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Supplies a custom IO handler to the importer to use to open and
|
/** Supplies a custom IO handler to the importer to use to open and
|
||||||
* access files. If you need the importer to use custom IO logic to
|
* access files. If you need the importer to use custom IO logic to
|
||||||
|
|
|
@ -547,6 +547,15 @@ enum aiComponent
|
||||||
// Various stuff to fine-tune the behaviour of specific importer plugins.
|
// Various stuff to fine-tune the behaviour of specific importer plugins.
|
||||||
// ###########################################################################
|
// ###########################################################################
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
/** @brief Importers which parse JSON may use this to obtain a pointer to a
|
||||||
|
* rapidjson::IRemoteSchemaDocumentProvider.
|
||||||
|
*
|
||||||
|
* The default value is nullptr
|
||||||
|
* Property type: void*
|
||||||
|
*/
|
||||||
|
#define AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER \
|
||||||
|
"IMPORT_SCHEMA_DOCUMENT_PROVIDER"
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** @brief Set whether the fbx importer will merge all geometry layers present
|
/** @brief Set whether the fbx importer will merge all geometry layers present
|
||||||
|
|
Loading…
Reference in New Issue