Merge pull request #42941 from RevoluPowered/fbx-3-2-2020

[3.2] FBX importer rewrite
This commit is contained in:
Rémi Verschelde 2020-10-30 17:34:28 +01:00 committed by GitHub
commit 4f908fb671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
284 changed files with 13848 additions and 72021 deletions

View File

@ -55,6 +55,13 @@ Comment: Godot Engine logo
Copyright: 2017, Andrea Calabró
License: CC-BY-3.0
Files: ./modules/fbx/fbx_parser/
Comment: Open Asset Import Library (assimp)
Copyright: 2006-2016, assimp team
2007-2020, Juan Linietsky, Ariel Manzur.
2014-2020, Godot Engine contributors.
License: BSD-3-clause and Expat
Files: ./platform/android/java/aidl/com/android/vending/billing/IInAppBillingService.aidl
./platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml
./platform/android/java/src/com/google/android/vending/expansion/downloader/*
@ -110,11 +117,6 @@ Copyright: 2007, Starbreeze Studios
2014-2020, Godot Engine contributors.
License: Expat and Zlib
Files: ./thirdparty/assimp/
Comment: Open Asset Import Library (assimp)
Copyright: 2006-2016, assimp team
License: BSD-3-clause
Files: ./thirdparty/bullet/
Comment: Bullet Continuous Collision Detection and Physics Library
Copyright: 2003-2013, Erwin Coumans

View File

@ -1,94 +0,0 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_assimp = env_modules.Clone()
# Force bundled version for now, there's no released version of Assimp with
# support for ArmaturePopulate which we use from their master branch.
if True: # env['builtin_assimp']:
thirdparty_dir = "#thirdparty/assimp"
env_assimp.Prepend(CPPPATH=["#thirdparty/assimp"])
env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/code"])
env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/include"])
# env_assimp.Append(CPPDEFINES=['ASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_SINGLETHREADED"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_BOOST_WORKAROUND"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OWN_ZLIB"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_EXPORT"])
# Importers we don't need
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3DS_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3MF_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AC_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AMF_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASE_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASSBIN_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_B3D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BLEND_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BVH_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_C4D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COB_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COLLADA_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_CSM_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_DXF_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF2_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_HMP_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IFC_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRR_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRRMESH_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWO_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWS_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_M3D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD2_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD3_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDC_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDL_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MMD_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MS3D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NDO_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NFF_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OBJ_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OFF_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OGRE_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OPENGEX_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_PLY_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3BSP_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_RAW_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SIB_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SMD_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STEP_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STL_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_TERRAGEN_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X3D_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_XGL_IMPORTER"])
env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X_IMPORTER"])
if env["platform"] == "windows":
env_assimp.Append(CPPDEFINES=["PLATFORM_WINDOWS"])
env_assimp.Append(CPPDEFINES=[("PLATFORM", "WINDOWS")])
elif env["platform"] == "x11":
env_assimp.Append(CPPDEFINES=["PLATFORM_LINUX"])
env_assimp.Append(CPPDEFINES=[("PLATFORM", "LINUX")])
elif env["platform"] == "osx":
env_assimp.Append(CPPDEFINES=["PLATFORM_DARWIN"])
env_assimp.Append(CPPDEFINES=[("PLATFORM", "DARWIN")])
env_thirdparty = env_assimp.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/CApi/*.cpp"))
env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Common/*.cpp"))
env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/PostProcessing/*.cpp"))
env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Material/*.cpp"))
env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/FBX/*.cpp"))
# Godot's own source files
env_assimp.add_source_files(env.modules_sources, "*.cpp")

File diff suppressed because it is too large Load Diff

View File

@ -1,262 +0,0 @@
rm -rf ../../thirdparty/assimp
cd ../../thirdparty/
git clone https://github.com/assimp/assimp.git
cd assimp
rm -rf code/3DSExporter.h
rm -rf code/3DSLoader.h
rm -rf code/3MFXmlTags.h
rm -rf code/ABCImporter.h
rm -rf code/ACLoader.h
rm -rf code/AMFImporter_Macro.hpp
rm -rf code/ASELoader.h
rm -rf code/assbin_chunks.h
rm -rf code/AssbinExporter.h
rm -rf code/AssbinLoader.h
rm -rf code/AssimpCExport.cpp
rm -rf code/AssxmlExporter.h
rm -rf code/B3DImporter.h
# rm -rf code/BaseProcess.cpp
# rm -rf code/BaseProcess.h
# rm -rf code/Bitmap.cpp
rm -rf code/BlenderBMesh.cpp
rm -rf code/BlenderBMesh.h
rm -rf code/BlenderCustomData.cpp
rm -rf code/BlenderCustomData.h
rm -rf code/BlenderIntermediate.h
rm -rf code/BlenderLoader.h
rm -rf code/BlenderModifier.h
rm -rf code/BlenderSceneGen.h
rm -rf code/BlenderTessellator.h
rm -rf code/BVHLoader.h
rm -rf code/C4DImporter.h
# rm -rf code/CalcTangentsProcess.h
# rm -rf code/CInterfaceIOWrapper.cpp
# rm -rf code/CInterfaceIOWrapper.h
rm -rf code/COBLoader.h
rm -rf code/COBScene.h
rm -rf code/ColladaExporter.h
rm -rf code/ColladaLoader.h
# rm -rf code/ComputeUVMappingProcess.h
# rm -rf code/ConvertToLHProcess.h
# rm -rf code/CreateAnimMesh.cpp
rm -rf code/CSMLoader.h
rm -rf code/D3MFExporter.h
rm -rf code/D3MFImporter.h
rm -rf code/D3MFOpcPackage.h
# rm -rf code/DeboneProcess.h
# rm -rf code/DefaultIOStream.cpp
# rm -rf code/DefaultIOSystem.cpp
# rm -rf code/DefaultProgressHandler.h
# rm -rf code/DropFaceNormalsProcess.cpp
# rm -rf code/DropFaceNormalsProcess.h
rm -rf code/DXFHelper.h
rm -rf code/DXFLoader.h
# rm -rf code/EmbedTexturesProcess.cpp
# rm -rf code/EmbedTexturesProcess.h
# rm -rf code/FBXCommon.h
# rm -rf code/FBXCompileConfig.h
# rm -rf code/FBXDeformer.cpp
# rm -rf code/FBXDocumentUtil.cpp
# rm -rf code/FBXDocumentUtil.h
# rm -rf code/FBXExporter.h
# rm -rf code/FBXExportNode.h
# rm -rf code/FBXExportProperty.h
# rm -rf code/FBXImporter.cpp
# rm -rf code/FBXImporter.h
# rm -rf code/FBXImportSettings.h
# rm -rf code/FBXMeshGeometry.h
# rm -rf code/FBXModel.cpp
# rm -rf code/FBXNodeAttribute.cpp
# rm -rf code/FBXParser.h
# rm -rf code/FBXProperties.cpp
# rm -rf code/FBXProperties.h
# rm -rf code/FBXTokenizer.cpp
# rm -rf code/FBXTokenizer.h
# rm -rf code/FBXUtil.cpp
# rm -rf code/FBXUtil.h
# rm -rf code/FileLogStream.h
# rm -rf code/FindDegenerates.h
# rm -rf code/FindInstancesProcess.h
# rm -rf code/FindInvalidDataProcess.h
rm -rf code/FIReader.hpp
# rm -rf code/FixNormalsStep.cpp
# rm -rf code/FixNormalsStep.h
# rm -rf code/GenFaceNormalsProcess.cpp
# rm -rf code/GenFaceNormalsProcess.h
# rm -rf code/GenVertexNormalsProcess.cpp
# rm -rf code/GenVertexNormalsProcess.h
rm -rf code/glTF2Asset.h
rm -rf code/glTF2Asset.inl
rm -rf code/glTF2AssetWriter.inl
rm -rf code/glTF2Exporter.cpp
rm -rf code/glTF2Importer.cpp
rm -rf code/glTF2AssetWriter.h
rm -rf code/glTFAsset.h
rm -rf code/glTFAsset.inl
rm -rf code/glTFAssetWriter.inl
rm -rf code/glTFExporter.cpp
rm -rf code/glTFImporter.cpp
rm -rf code/glTF2Exporter.h
rm -rf code/glTF2Importer.h
rm -rf code/glTFAssetWriter.h
rm -rf code/glTFExporter.h
rm -rf code/glTFImporter.h
rm -rf code/HalfLifeFileData.h
rm -rf code/HMPFileData.h
rm -rf code/HMPLoader.h
rm -rf code/HMPLoader.cpp
rm -rf code/IFF.h
# rm -rf code/Importer.h
# rm -rf code/ImproveCacheLocality.h
rm -rf code/IRRLoader.h
rm -rf code/IRRMeshLoader.h
rm -rf code/IRRShared.h
# rm -rf code/JoinVerticesProcess.h
# rm -rf code/LimitBoneWeightsProcess.cpp
# rm -rf code/LimitBoneWeightsProcess.h
rm -rf code/LWSLoader.h
rm -rf code/makefile.mingw
# rm -rf code/MakeVerboseFormat.cpp
# rm -rf code/MakeVerboseFormat.h
# rm -rf code/MaterialSystem.h
rm -rf code/MD2FileData.h
rm -rf code/MD2Loader.h
rm -rf code/MD2NormalTable.h
rm -rf code/MD3FileData.h
rm -rf code/MD3Loader.h
rm -rf code/MD4FileData.h
rm -rf code/MD5Loader.h
rm -rf code/MD5Parser.cpp
rm -rf code/MDCFileData.h
rm -rf code/MDCLoader.h
rm -rf code/MDLDefaultColorMap.h
# rm -rf code/MMDCpp14.h
# rm -rf code/MMDImporter.h
rm -rf code/MS3DLoader.h
rm -rf code/NDOLoader.h
rm -rf code/NFFLoader.h
rm -rf code/ObjExporter.h
rm -rf code/ObjFileImporter.h
rm -rf code/ObjFileMtlImporter.h
rm -rf code/ObjFileParser.h
rm -rf code/ObjTools.h
rm -rf code/ObjExporter.cpp
rm -rf code/ObjFileImporter.cpp
rm -rf code/ObjFileMtlImporter.cpp
rm -rf code/ObjFileParser.cpp
rm -rf code/OFFLoader.h
rm -rf code/OFFLoader.cpp
rm -rf code/OgreImporter.cpp
rm -rf code/OgreImporter.h
rm -rf code/OgreParsingUtils.h
rm -rf code/OgreXmlSerializer.h
rm -rf code/OgreXmlSerializer.cpp
rm -rf code/OgreBinarySerializer.cpp
rm -rf code/OpenGEXExporter.cpp
rm -rf code/OpenGEXExporter.h
rm -rf code/OpenGEXImporter.h
rm -rf code/OpenGEXStructs.h
rm -rf code/OpenGEXImporter.cpp
# rm -rf code/OptimizeGraph.h
# rm -rf code/OptimizeMeshes.cpp
# rm -rf code/OptimizeMeshes.h
rm -rf code/PlyExporter.h
rm -rf code/PlyLoader.h
# rm -rf code/PolyTools.h
# rm -rf code/PostStepRegistry.cpp
# rm -rf code/PretransformVertices.h
rm -rf code/Q3BSPFileData.h
rm -rf code/Q3BSPFileImporter.h
rm -rf code/Q3BSPFileParser.cpp
rm -rf code/Q3BSPFileParser.h
rm -rf code/Q3BSPZipArchive.cpp
rm -rf code/Q3BSPZipArchive.h
rm -rf code/Q3DLoader.h
rm -rf code/Q3DLoader.cpp
rm -rf code/Q3BSPFileImporter.cpp
rm -rf code/RawLoader.h
# rm -rf code/RemoveComments.cpp
# rm -rf code/RemoveRedundantMaterials.cpp
# rm -rf code/RemoveRedundantMaterials.h
# rm -rf code/RemoveVCProcess.h
# rm -rf code/ScaleProcess.cpp
# rm -rf code/ScaleProcess.h
# rm -rf code/scene.cpp
# rm -rf code/ScenePreprocessor.cpp
# rm -rf code/ScenePreprocessor.h
# rm -rf code/ScenePrivate.h
# rm -rf code/SGSpatialSort.cpp
rm -rf code/SIBImporter.h
rm -rf code/SMDLoader.cpp
# rm -rf code/simd.cpp
# rm -rf code/simd.h
# rm -rf code/SortByPTypeProcess.h
# rm -rf code/SplitByBoneCountProcess.h
# rm -rf code/SplitLargeMeshes.h
# rm -rf code/StdOStreamLogStream.h
rm -rf code/StepExporter.h
rm -rf code/StepExporter.cpp
rm -rf code/STLExporter.cpp
rm -rf code/STLExporter.h
rm -rf code/STLLoader.h
rm -rf code/STLLoader.cpp
# rm -rf code/TargetAnimation.cpp
# rm -rf code/TargetAnimation.h
rm -rf code/TerragenLoader.h
rm -rf code/TerragenLoader.cpp
# rm -rf code/TextureTransform.h
# rm -rf code/TriangulateProcess.h
rm -rf code/UnrealLoader.h
# rm -rf code/ValidateDataStructure.h
# rm -rf code/Version.cpp
# rm -rf code/VertexTriangleAdjacency.cpp
# rm -rf code/VertexTriangleAdjacency.h
# rm -rf code/Win32DebugLogStream.h
rm -rf code/X3DImporter_Macro.hpp
rm -rf code/X3DImporter_Metadata.cpp
rm -rf code/X3DImporter_Networking.cpp
rm -rf code/X3DImporter_Texturing.cpp
rm -rf code/X3DImporter_Shape.cpp
rm -rf code/X3DImporter_Rendering.cpp
rm -rf code/X3DImporter_Postprocess.cpp
rm -rf code/X3DImporter_Light.cpp
rm -rf code/X3DImporter_Group.cpp
rm -rf code/X3DImporter_Geometry3D.cpp
rm -rf code/X3DImporter_Geometry2D.cpp
rm -rf code/X3DImporter.cpp
rm -rf code/X3DExporter.cpp
rm -rf code/X3DVocabulary.cpp
rm -rf code/XFileExporter.h
rm -rf code/XFileExporter.cpp
rm -rf code/XFileHelper.h
rm -rf code/XFileHelper.cpp
rm -rf code/XFileImporter.h
rm -rf code/XFileImporter.cpp
rm -rf code/XFileParser.h
rm -rf code/XFileParser.cpp
rm -rf code/XGLLoader.h
rm -rf code/XGLLoader.cpp
rm -rf code/Importer
rm -rf .git
rm -rf cmake-modules
rm -rf doc
rm -rf packaging
rm -rf port
rm -rf samples
rm -rf scripts
rm -rf test
rm -rf tools
rm -rf contrib/zlib
rm -rf contrib/android-cmake
rm -rf contrib/gtest
rm -rf contrib/clipper
rm -rf contrib/irrXML
rm -rf contrib/Open3DGC
rm -rf contrib/openddlparser
rm -rf contrib/poly2tri
#rm -rf contrib/rapidjson
rm -rf contrib/unzip
rm -rf contrib/zip
rm -rf contrib/stb_image
rm .travis*

View File

@ -1,447 +0,0 @@
/*************************************************************************/
/* import_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef IMPORT_UTILS_IMPORTER_ASSIMP_H
#define IMPORT_UTILS_IMPORTER_ASSIMP_H
#include "core/io/image_loader.h"
#include "import_state.h"
#include <assimp/SceneCombiner.h>
#include <assimp/cexport.h>
#include <assimp/cimport.h>
#include <assimp/matrix4x4.h>
#include <assimp/pbrmaterial.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/LogStream.hpp>
#include <assimp/Logger.hpp>
#include <string>
using namespace AssimpImporter;
#define AI_PROPERTIES aiTextureType_UNKNOWN, 0
#define AI_NULL 0, 0
#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor"
#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness"
#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness"
#define AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE "$raw.Maya|emissionColor|file"
#define AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR "$raw.Maya|emission"
#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file"
#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo"
#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file"
#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo"
#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file"
#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo"
#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling"
#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color"
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive"
#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic"
#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness"
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity"
#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file"
#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo"
#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file"
#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo"
#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file"
#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo"
#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file"
#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo"
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file"
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo"
#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file"
#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo"
/**
* Assimp Utils
* Conversion tools / glue code to convert from assimp to godot
*/
class AssimpUtils {
public:
/**
* calculate tangents for mesh data from assimp data
*/
static void calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) {
const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index];
const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index];
const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z);
const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index];
const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d);
w[index] = plane_tangent;
}
struct AssetImportFbx {
enum ETimeMode {
TIME_MODE_DEFAULT = 0,
TIME_MODE_120 = 1,
TIME_MODE_100 = 2,
TIME_MODE_60 = 3,
TIME_MODE_50 = 4,
TIME_MODE_48 = 5,
TIME_MODE_30 = 6,
TIME_MODE_30_DROP = 7,
TIME_MODE_NTSC_DROP_FRAME = 8,
TIME_MODE_NTSC_FULL_FRAME = 9,
TIME_MODE_PAL = 10,
TIME_MODE_CINEMA = 11,
TIME_MODE_1000 = 12,
TIME_MODE_CINEMA_ND = 13,
TIME_MODE_CUSTOM = 14,
TIME_MODE_TIME_MODE_COUNT = 15
};
enum UpAxis {
UP_VECTOR_AXIS_X = 1,
UP_VECTOR_AXIS_Y = 2,
UP_VECTOR_AXIS_Z = 3
};
enum FrontAxis {
FRONT_PARITY_EVEN = 1,
FRONT_PARITY_ODD = 2,
};
enum CoordAxis {
COORD_RIGHT = 0,
COORD_LEFT = 1
};
};
/** Get assimp string
* automatically filters the string data
*/
static String get_assimp_string(const aiString &p_string) {
//convert an assimp String to a Godot String
String name;
name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
if (name.find(":") != -1) {
String replaced_name = name.split(":")[1];
print_verbose("Replacing " + name + " containing : with " + replaced_name);
name = replaced_name;
}
return name;
}
static String get_anim_string_from_assimp(const aiString &p_string) {
String name;
name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
if (name.find(":") != -1) {
String replaced_name = name.split(":")[1];
print_verbose("Replacing " + name + " containing : with " + replaced_name);
name = replaced_name;
}
return name;
}
/**
* No filter logic get_raw_string_from_assimp
* This just convers the aiString to a parsed utf8 string
* Without removing special chars etc
*/
static String get_raw_string_from_assimp(const aiString &p_string) {
String name;
name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
return name;
}
static Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
return Ref<Animation>();
}
/**
* Converts aiMatrix4x4 to godot Transform
*/
static const Transform assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
aiMatrix4x4 matrix = p_matrix;
Transform xform;
xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
return xform;
}
/** Get fbx fps for time mode meta data
*/
static float get_fbx_fps(int32_t time_mode, const aiScene *p_scene) {
switch (time_mode) {
case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack
case AssetImportFbx::TIME_MODE_120: return 120;
case AssetImportFbx::TIME_MODE_100: return 100;
case AssetImportFbx::TIME_MODE_60: return 60;
case AssetImportFbx::TIME_MODE_50: return 50;
case AssetImportFbx::TIME_MODE_48: return 48;
case AssetImportFbx::TIME_MODE_30: return 30;
case AssetImportFbx::TIME_MODE_30_DROP: return 30;
case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
case AssetImportFbx::TIME_MODE_PAL: return 25;
case AssetImportFbx::TIME_MODE_CINEMA: return 24;
case AssetImportFbx::TIME_MODE_1000: return 1000;
case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
case AssetImportFbx::TIME_MODE_CUSTOM:
int32_t frame_rate = -1;
p_scene->mMetaData->Get("FrameRate", frame_rate);
return frame_rate;
}
return 0;
}
/**
* Get global transform for the current node - so we can use world space rather than
* local space coordinates
* useful if you need global - although recommend using local wherever possible over global
* as you could break fbx scaling :)
*/
static Transform _get_global_assimp_node_transform(const aiNode *p_current_node) {
aiNode const *current_node = p_current_node;
Transform xform;
while (current_node != NULL) {
xform = assimp_matrix_transform(current_node->mTransformation) * xform;
current_node = current_node->mParent;
}
return xform;
}
/**
* Find hardcoded textures from assimp which could be in many different directories
*/
static void find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) {
Vector<String> paths;
paths.push_back(path.get_basename() + extension);
paths.push_back(path + extension);
paths.push_back(path);
paths.push_back(p_path.get_base_dir().plus_file(path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file(path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file(path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file()));
paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file().get_basename() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file() + extension));
paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file()));
for (int i = 0; i < paths.size(); i++) {
if (dir.file_exists(paths[i])) {
found = true;
path = paths[i];
return;
}
}
}
/** find the texture path for the supplied fbx path inside godot
* very simple lookup for subfolders etc for a texture which may or may not be in a directory
*/
static void find_texture_path(const String &r_p_path, String &r_path, bool &r_found) {
_Directory dir;
List<String> exts;
ImageLoader::get_recognized_extensions(&exts);
Vector<String> split_path = r_path.get_basename().split("*");
if (split_path.size() == 2) {
r_found = true;
return;
}
if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) {
r_path = r_p_path.get_base_dir() + r_path.get_file();
r_found = true;
return;
}
for (int32_t i = 0; i < exts.size(); i++) {
if (r_found) {
return;
}
find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]);
}
}
/**
* set_texture_mapping_mode
* Helper to check the mapping mode of the texture (repeat, clamp and mirror)
*/
static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
ERR_FAIL_COND(texture.is_null());
ERR_FAIL_COND(map_mode == NULL);
aiTextureMapMode tex_mode = map_mode[0];
int32_t flags = Texture::FLAGS_DEFAULT;
if (tex_mode == aiTextureMapMode_Wrap) {
//Default
} else if (tex_mode == aiTextureMapMode_Clamp) {
flags = flags & ~Texture::FLAG_REPEAT;
} else if (tex_mode == aiTextureMapMode_Mirror) {
flags = flags | Texture::FLAG_MIRRORED_REPEAT;
}
texture->set_flags(flags);
}
/**
* Load or load from cache image :)
*/
static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path) {
Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
// if our cache contains this image then don't bother
if (match) {
return match->get();
}
Vector<String> split_path = p_path.get_basename().split("*");
if (split_path.size() == 2) {
size_t texture_idx = split_path[1].to_int();
ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
aiTexture *tex = p_scene->mTextures[texture_idx];
String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
filename = filename.get_file();
print_verbose("Open Asset Import: Loading embedded texture " + filename);
if (tex->mHeight == 0) {
if (tex->CheckFormat("png")) {
ERR_FAIL_COND_V(Image::_png_mem_loader_func == NULL, Ref<Image>());
Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
state.path_to_image_cache.insert(p_path, img);
return img;
} else if (tex->CheckFormat("jpg")) {
ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == NULL, Ref<Image>());
Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
state.path_to_image_cache.insert(p_path, img);
return img;
} else if (tex->CheckFormat("dds")) {
ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented");
}
} else {
Ref<Image> img;
img.instance();
PoolByteArray arr;
uint32_t size = tex->mWidth * tex->mHeight;
arr.resize(size);
memcpy(arr.write().ptr(), tex->pcData, size);
ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
//ARGB8888 to RGBA8888
for (int32_t i = 0; i < arr.size() / 4; i++) {
arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
}
img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
state.path_to_image_cache.insert(p_path, img);
return img;
}
return Ref<Image>();
} else {
Ref<Texture> texture = ResourceLoader::load(p_path);
ERR_FAIL_COND_V(texture.is_null(), Ref<Image>());
Ref<Image> image = texture->get_data();
ERR_FAIL_COND_V(image.is_null(), Ref<Image>());
state.path_to_image_cache.insert(p_path, image);
return image;
}
return Ref<Image>();
}
/* create texture from assimp data, if found in path */
static bool CreateAssimpTexture(
AssimpImporter::ImportState &state,
aiString texture_path,
String &filename,
String &path,
AssimpImageData &image_state) {
filename = get_raw_string_from_assimp(texture_path);
path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
bool found = false;
find_texture_path(state.path, path, found);
if (found) {
image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
if (image_state.raw_image.is_valid()) {
image_state.texture.instance();
image_state.texture->create_from_image(image_state.raw_image);
image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
return true;
}
}
return false;
}
/** GetAssimpTexture
* Designed to retrieve textures for you
*/
static bool GetAssimpTexture(
AssimpImporter::ImportState &state,
aiMaterial *ai_material,
aiTextureType texture_type,
String &filename,
String &path,
AssimpImageData &image_state) {
aiString ai_filename = aiString();
if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) {
return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
}
return false;
}
};
#endif // IMPORT_UTILS_IMPORTER_ASSIMP_H

15
modules/fbx/SCsub Normal file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_fbx = env_modules.Clone()
# Make includes relative to the folder path specified here so our includes are clean
env_fbx.Prepend(CPPPATH=["#modules/fbx/"])
# Godot's own source files
env_fbx.add_source_files(env.modules_sources, "tools/*.cpp")
env_fbx.add_source_files(env.modules_sources, "data/*.cpp")
env_fbx.add_source_files(env.modules_sources, "fbx_parser/*.cpp")
env_fbx.add_source_files(env.modules_sources, "*.cpp")

View File

@ -0,0 +1,46 @@
/*************************************************************************/
/* fbx_anim_container.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_ANIM_CONTAINER_H
#define FBX_ANIM_CONTAINER_H
#include "core/vector.h"
// Generic keyframes 99.99 percent of files will be vector3, except if quat interp is used, or visibility tracks
// FBXTrack is used in a map in the implementation in fbx/editor_scene_importer_fbx.cpp
// to avoid having to rewrite the entire logic I refactored this into the code instead.
// once it works I can rewrite so we can add the fun misc features / small features
struct FBXTrack {
bool has_default = false;
Vector3 default_value;
std::map<int64_t, Vector3> keyframes;
};
#endif //MODEL_ABSTRACTION_ANIM_CONTAINER_H

View File

@ -0,0 +1,93 @@
/*************************************************************************/
/* fbx_bone.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "fbx_bone.h"
#include "fbx_node.h"
#include "import_state.h"
Ref<FBXNode> FBXBone::get_link(const ImportState &state) const {
print_verbose("bone name: " + bone_name);
ERR_FAIL_COND_V_MSG(cluster == nullptr, nullptr, "bone has invalid cluster");
ERR_FAIL_COND_V_MSG(cluster->TargetNode() == nullptr, nullptr, "bone has invalid target node");
Ref<FBXNode> link_node;
uint64_t id = cluster->TargetNode()->ID();
if (state.fbx_target_map.has(id)) {
link_node = state.fbx_target_map[id];
} else {
print_error("link node not found for " + itos(id));
}
// the node in space this is for, like if it's FOR a target.
return link_node;
}
/* right now we just get single skin working and we can patch in the multiple tomorrow - per skin not per bone. */
// this will work for multiple meshes :) awesomeness.
// okay so these formula's are complex and need proper understanding of
// shear, pivots, geometric pivots, pre rotation and post rotation
// additionally DO NOT EDIT THIS if your blender file isn't working.
// Contact RevoluPowered Gordon MacPherson if you are contemplating making edits to this.
Transform FBXBone::get_vertex_skin_xform(const ImportState &state, Transform mesh_global_position, bool &r_valid_pose) {
r_valid_pose = false;
print_verbose("get_vertex_skin_xform: " + bone_name);
ERR_FAIL_COND_V_MSG(cluster == nullptr, Transform(), "[serious] unable to resolve the fbx cluster for this bone " + bone_name);
// these methods will ONLY work for Maya.
if (cluster->TransformAssociateModelValid()) {
//print_error("additive skinning in use");
Transform associate_global_init_position = cluster->TransformAssociateModel();
Transform associate_global_current_position = Transform();
Transform reference_global_init_position = cluster->GetTransform();
Transform cluster_global_init_position = cluster->TransformLink();
Ref<FBXNode> link_node = get_link(state);
ERR_FAIL_COND_V_MSG(link_node.is_null(), Transform(), "invalid link corrupt file detected");
r_valid_pose = true;
Transform cluster_global_current_position = link_node.is_valid() && link_node->pivot_transform.is_valid() ? link_node->pivot_transform->GlobalTransform : Transform();
vertex_transform_matrix = reference_global_init_position.affine_inverse() * associate_global_init_position * associate_global_current_position.affine_inverse() *
cluster_global_current_position * cluster_global_init_position.affine_inverse() * reference_global_init_position;
} else {
//print_error("non additive skinning is in use");
Transform reference_global_position = cluster->GetTransform();
Transform reference_global_current_position = mesh_global_position;
//Transform geometric_pivot = Transform(); // we do not use this - 3ds max only
Transform global_init_position = cluster->TransformLink();
if (global_init_position.basis.determinant() == 0) {
global_init_position = Transform(Basis(), global_init_position.origin);
}
Transform cluster_relative_init_position = global_init_position.affine_inverse() * reference_global_position;
Transform cluster_relative_position_inverse = reference_global_current_position.affine_inverse() * global_init_position;
vertex_transform_matrix = cluster_relative_position_inverse * cluster_relative_init_position;
r_valid_pose = true;
}
return vertex_transform_matrix;
}

View File

@ -0,0 +1,95 @@
/*************************************************************************/
/* fbx_bone.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_BONE_H
#define FBX_BONE_H
#include "fbx_node.h"
#include "import_state.h"
#include "fbx_parser/FBXDocument.h"
struct PivotTransform;
struct FBXBone : public Reference {
uint64_t parent_bone_id = 0;
uint64_t bone_id = 0;
bool valid_parent = false; // if the parent bone id is set up.
String bone_name = String(); // bone name
bool is_root_bone() const {
return !valid_parent;
}
uint64_t target_node_id; // the node target id for the skeleton element
bool valid_target = false; // only applies to bones with a mesh / in the skin.
// Godot specific data
int godot_bone_id = -2; // godot internal bone id assigned after import
// if a bone / armature is the root then FBX skeleton will contain the bone not any other skeleton.
// this is to support joints by themselves in scenes
bool valid_armature_id = false;
uint64_t armature_id = 0;
// Vertex Weight information
Transform transform_link; // todo remove
Transform transform_matrix; // todo remove
/* get associate model - the model can be invalid sometimes */
Ref<FBXBone> get_associate_model() const {
return parent_bone;
}
/* link node is the parent bone */
Ref<FBXNode> get_link(const ImportState &state) const;
Transform get_vertex_skin_xform(const ImportState &state, Transform mesh_global_position, bool &valid);
Transform vertex_transform_matrix;
Transform local_cluster_matrix; // set_bone_pose
mutable const FBXDocParser::Deformer *skin = nullptr;
mutable const FBXDocParser::Cluster *cluster = nullptr;
mutable const FBXDocParser::Geometry *geometry = nullptr;
mutable const FBXDocParser::ModelLimbNode *limb_node = nullptr;
void set_pivot_xform(Ref<PivotTransform> p_pivot_xform) {
pivot_xform = p_pivot_xform;
}
// pose node / if assigned
Transform pose_node = Transform();
bool assigned_pose_node = false;
Ref<FBXBone> parent_bone = Ref<FBXBone>();
Ref<PivotTransform> pivot_xform = Ref<PivotTransform>();
Ref<FBXSkeleton> fbx_skeleton = Ref<FBXSkeleton>();
};
#endif // FBX_BONE_H

View File

@ -0,0 +1,487 @@
/*************************************************************************/
/* fbx_material.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "fbx_material.h"
#include "scene/resources/material.h"
#include "scene/resources/texture.h"
String FBXMaterial::get_material_name() const {
return material_name;
}
void FBXMaterial::set_imported_material(const FBXDocParser::Material *p_material) {
material = p_material;
}
void FBXMaterial::add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths) {
if (search_directory.empty()) {
texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(p_filename));
} else {
texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(search_directory + "/" + p_filename));
texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file("../" + search_directory + "/" + p_filename));
}
}
String find_file(const String &p_base, const String &p_file_to_find) {
_Directory dir;
dir.open(p_base);
dir.list_dir_begin();
String n = dir.get_next();
while (n != String()) {
if (n == "." || n == "..") {
n = dir.get_next();
continue;
}
if (dir.current_is_dir()) {
// Don't use `path_to` or the returned path will be wrong.
const String f = find_file(p_base + "/" + n, p_file_to_find);
if (f != "") {
return f;
}
} else if (n == p_file_to_find) {
return p_base + "/" + n;
}
n = dir.get_next();
}
dir.list_dir_end();
return String();
}
// fbx will not give us good path information and let's not regex them to fix them
// no relative paths are in fbx generally they have a rel field but it's populated incorrectly by the SDK.
String FBXMaterial::find_texture_path_by_filename(const String p_filename, const String p_current_directory) {
_Directory dir;
Vector<String> paths;
add_search_string(p_filename, p_current_directory, "", paths);
add_search_string(p_filename, p_current_directory, "texture", paths);
add_search_string(p_filename, p_current_directory, "textures", paths);
add_search_string(p_filename, p_current_directory, "Textures", paths);
add_search_string(p_filename, p_current_directory, "materials", paths);
add_search_string(p_filename, p_current_directory, "mats", paths);
add_search_string(p_filename, p_current_directory, "pictures", paths);
add_search_string(p_filename, p_current_directory, "images", paths);
for (int i = 0; i < paths.size(); i++) {
if (dir.file_exists(paths[i])) {
return paths[i];
}
}
// We were not able to find the texture in the common locations,
// try to find it into the project globally.
// The common textures can be stored into one of those folders:
// res://asset
// res://texture
// res://material
// res://mat
// res://image
// res://picture
//
// Note the folders can also be called with custom names, like:
// res://my_assets
// since the keyword `asset` is into the directory name the textures will be
// searched there too.
dir.open("res://");
dir.list_dir_begin();
String n = dir.get_next();
while (n != String()) {
if (n == "." || n == "..") {
n = dir.get_next();
continue;
}
if (dir.current_is_dir()) {
const String lower_n = n.to_lower();
if (
// Don't need to use plural.
lower_n.find("asset") >= 0 ||
lower_n.find("texture") >= 0 ||
lower_n.find("material") >= 0 ||
lower_n.find("mat") >= 0 ||
lower_n.find("image") >= 0 ||
lower_n.find("picture") >= 0) {
// Don't use `path_to` or the returned path will be wrong.
const String f = find_file(String("res://") + n, p_filename);
if (f != "") {
return f;
}
}
}
n = dir.get_next();
}
dir.list_dir_end();
return "";
}
FBXMaterial::MaterialInfo FBXMaterial::extract_material_info(const FBXDocParser::Material *material) const {
MaterialInfo mat_info;
// TODO Layered textures are a collection on textures stored into an array.
// Extract layered textures is not yet supported. Per each texture in the
// layered texture array you want to use the below method to extract those.
for (std::pair<std::string, const FBXDocParser::Texture *> texture : material->Textures()) {
const std::string &fbx_mapping_name = texture.first;
if (fbx_feature_mapping_desc.count(fbx_mapping_name) > 0) {
// This is a feature not a normal texture.
mat_info.features.push_back(fbx_feature_mapping_desc.at(fbx_mapping_name));
continue;
}
ERR_CONTINUE_MSG(fbx_texture_mapping_desc.count(fbx_mapping_name) <= 0, "This FBX has a material with mapping name: " + String(fbx_mapping_name.c_str()) + " which is not yet supported by this importer. Consider open an issue so we can support it.");
const String absoulte_fbx_file_path = texture.second->FileName().c_str();
const String file_extension = absoulte_fbx_file_path.get_extension().to_upper();
const String file_extension_uppercase = file_extension.to_upper();
// TODO: we don't support EMBED for DDS and TGA.
ERR_CONTINUE_MSG(
file_extension_uppercase != "PNG" &&
file_extension_uppercase != "JPEG" &&
file_extension_uppercase != "JPG" &&
file_extension_uppercase != "TGA" &&
file_extension_uppercase != "WEBP" &&
file_extension_uppercase != "DDS",
"The FBX file contains a texture with an unrecognized extension: " + file_extension_uppercase);
const String texture_name = absoulte_fbx_file_path.get_file();
const SpatialMaterial::TextureParam mapping_mode = fbx_texture_mapping_desc.at(fbx_mapping_name);
TextureFileMapping file_mapping;
file_mapping.map_mode = mapping_mode;
file_mapping.name = texture_name;
file_mapping.texture = texture.second;
mat_info.textures.push_back(file_mapping);
// Make sure to active the various features.
switch (mapping_mode) {
case SpatialMaterial::TextureParam::TEXTURE_ALBEDO:
case SpatialMaterial::TextureParam::TEXTURE_METALLIC:
case SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS:
case SpatialMaterial::TextureParam::TEXTURE_FLOWMAP:
case SpatialMaterial::TextureParam::TEXTURE_REFRACTION:
case SpatialMaterial::TextureParam::TEXTURE_MAX:
// No features required.
break;
case SpatialMaterial::TextureParam::TEXTURE_EMISSION:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_EMISSION);
break;
case SpatialMaterial::TextureParam::TEXTURE_NORMAL:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING);
break;
case SpatialMaterial::TextureParam::TEXTURE_RIM:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_RIM);
break;
case SpatialMaterial::TextureParam::TEXTURE_CLEARCOAT:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_CLEARCOAT);
break;
case SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_AMBIENT_OCCLUSION);
break;
case SpatialMaterial::TextureParam::TEXTURE_DEPTH:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_DEPTH_MAPPING);
break;
case SpatialMaterial::TextureParam::TEXTURE_SUBSURFACE_SCATTERING:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_SUBSURACE_SCATTERING);
break;
case SpatialMaterial::TextureParam::TEXTURE_TRANSMISSION:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_TRANSMISSION);
break;
case SpatialMaterial::TextureParam::TEXTURE_DETAIL_ALBEDO:
case SpatialMaterial::TextureParam::TEXTURE_DETAIL_MASK:
case SpatialMaterial::TextureParam::TEXTURE_DETAIL_NORMAL:
mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_DETAIL);
break;
}
}
return mat_info;
}
template <class T>
T extract_from_prop(FBXDocParser::PropertyPtr prop, const T &p_default, const std::string &p_name, const String &p_type) {
ERR_FAIL_COND_V_MSG(prop == nullptr, p_default, "invalid property passed to extractor");
const FBXDocParser::TypedProperty<T> *val = dynamic_cast<const FBXDocParser::TypedProperty<T> *>(prop);
ERR_FAIL_COND_V_MSG(val == nullptr, p_default, "The FBX is corrupted, the property `" + String(p_name.c_str()) + "` is a `" + String(typeid(*prop).name()) + "` but should be a " + p_type);
// Make sure to not lost any eventual opacity.
return val->Value();
}
Ref<SpatialMaterial> FBXMaterial::import_material(ImportState &state) {
ERR_FAIL_COND_V(material == nullptr, nullptr);
const String p_fbx_current_directory = state.path;
Ref<SpatialMaterial> spatial_material;
// read the material file
// is material two sided
// read material name
print_verbose("[material] material name: " + ImportUtils::FBXNodeToName(material->Name()));
material_name = ImportUtils::FBXNodeToName(material->Name());
// Extract info.
MaterialInfo material_info = extract_material_info(material);
// Extract other parameters info.
for (FBXDocParser::LazyPropertyMap::value_type iter : material->Props()->GetLazyProperties()) {
const std::string name = iter.first;
//const Assimp::FBX::ElementPtr element = iter.second;
if (name.empty()) {
continue;
}
PropertyDesc desc = PROPERTY_DESC_NOT_FOUND;
if (fbx_properties_desc.count(name) > 0) {
desc = fbx_properties_desc.at(name);
}
if (desc == PROPERTY_DESC_IGNORE) {
print_verbose("The FBX material parameter: `" + String(name.c_str()) + "` is ignored.");
continue;
} else {
print_verbose("FBX Material parameter: " + String(name.c_str()));
}
if (desc == PROPERTY_DESC_NOT_FOUND) {
continue;
}
ERR_CONTINUE_MSG(desc == PROPERTY_DESC_NOT_FOUND, "The FBX material parameter: `" + String(name.c_str()) + "` was not recognized. Please open an issue so we can add the support to it.");
FBXDocParser::PropertyPtr prop = material->Props()->Get(name);
//Assimp::FBX::PropertyPtr prop = prop.second.
if (prop == nullptr) {
continue;
}
ERR_CONTINUE_MSG(prop == nullptr, "This file may be corrupted because is not possible to extract the material parameter: " + String(name.c_str()));
if (spatial_material.is_null()) {
// Done here so if no data no material is created.
spatial_material.instance();
}
switch (desc) {
case PROPERTY_DESC_ALBEDO_COLOR: {
const Vector3 color = extract_from_prop(prop, Vector3(0, 0, 0), name, "Vector3");
// Make sure to not lost any eventual opacity.
Color c = spatial_material->get_albedo();
c[0] = color[0];
c[1] = color[1];
c[2] = color[2];
spatial_material->set_albedo(c);
} break;
case PROPERTY_DESC_TRANSPARENT: {
const real_t opacity = extract_from_prop(prop, 1.0f, name, "float");
if (opacity < (1.0 - CMP_EPSILON)) {
Color c = spatial_material->get_albedo();
c[3] = opacity;
spatial_material->set_albedo(c);
material_info.features.push_back(SpatialMaterial::Feature::FEATURE_TRANSPARENT);
spatial_material->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
}
} break;
case PROPERTY_DESC_METALLIC: {
spatial_material->set_metallic(std::min(1.0f, extract_from_prop(prop, 1.0f, name, "float")));
} break;
case PROPERTY_DESC_ROUGHNESS: {
spatial_material->set_roughness(std::min(1.0f, extract_from_prop(prop, 1.0f, name, "float")));
} break;
case PROPERTY_DESC_COAT: {
spatial_material->set_clearcoat(extract_from_prop(prop, 1.0f, name, "float"));
material_info.features.push_back(SpatialMaterial::Feature::FEATURE_CLEARCOAT);
} break;
case PROPERTY_DESC_COAT_ROUGHNESS: {
spatial_material->set_clearcoat_gloss(1.0 - extract_from_prop(prop, 0.5f, name, "float"));
material_info.features.push_back(SpatialMaterial::Feature::FEATURE_CLEARCOAT);
} break;
case PROPERTY_DESC_EMISSIVE: {
const real_t emissive = extract_from_prop(prop, 0.0f, name, "float");
if (emissive > CMP_EPSILON) {
spatial_material->set_emission_energy(emissive);
material_info.features.push_back(SpatialMaterial::Feature::FEATURE_EMISSION);
}
} break;
case PROPERTY_DESC_EMISSIVE_COLOR: {
const Vector3 color = extract_from_prop(prop, Vector3(0, 0, 0), name, "Vector3");
Color c;
c[0] = color[0];
c[1] = color[1];
c[2] = color[2];
spatial_material->set_emission(c);
} break;
case PROPERTY_DESC_NOT_FOUND:
case PROPERTY_DESC_IGNORE:
// Already checked, can't happen.
CRASH_NOW();
break;
}
}
// Set the material features.
for (int x = 0; x < material_info.features.size(); x++) {
if (spatial_material.is_null()) {
// Done here so if no textures no material is created.
spatial_material.instance();
}
spatial_material->set_feature(material_info.features[x], true);
}
// Set the textures.
for (int x = 0; x < material_info.textures.size(); x++) {
TextureFileMapping mapping = material_info.textures[x];
Ref<Texture> texture;
print_verbose("texture mapping name: " + mapping.name);
if (state.cached_image_searches.has(mapping.name)) {
texture = state.cached_image_searches[mapping.name];
} else {
String path = find_texture_path_by_filename(mapping.name, p_fbx_current_directory);
if (!path.empty()) {
Error err;
Ref<Texture> image_texture = ResourceLoader::load(path, "Texture", false, &err);
ERR_CONTINUE_MSG(err != OK, "unable to import image file not loaded yet: " + path);
ERR_CONTINUE(image_texture == NULL || image_texture.is_null());
texture = image_texture;
state.cached_image_searches.insert(mapping.name, texture);
print_verbose("Created texture from loaded image file.");
} else if (mapping.texture != nullptr && mapping.texture->Media() != nullptr) {
// This is an embedded texture. Extract it.
Ref<Image> image;
image.instance();
const String extension = mapping.name.get_extension().to_upper();
if (extension == "PNG") {
// The stored file is a PNG.
image = Image::_png_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded PNG image load fail.");
} else if (
extension == "JPEG" ||
extension == "JPG") {
// The stored file is a JPEG.
image = Image::_jpg_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded JPEG image load fail.");
} else if (extension == "TGA") {
// The stored file is a TGA.
//image = Image::_tga_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
//ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded TGA image load fail.");
} else if (extension == "WEBP") {
// The stored file is a WEBP.
image = Image::_webp_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded WEBP image load fail.");
// } else if (extension == "DDS") {
// // In this moment is not possible to extract a DDS from a buffer, TODO consider add it to godot. See `textureloader_dds.cpp::load().
// // The stored file is a DDS.
} else {
ERR_CONTINUE_MSG(true, "The embedded image with extension: " + extension + " is not yet supported. Open an issue please.");
}
Ref<ImageTexture> image_texture;
image_texture.instance();
image_texture->create_from_image(image);
const int32_t flags = Texture::FLAGS_DEFAULT;
image_texture->set_flags(flags);
texture = image_texture;
state.cached_image_searches[mapping.name] = texture;
print_verbose("Created texture from embedded image.");
} else {
ERR_CONTINUE_MSG(true, "The FBX texture, with name: `" + mapping.name + "`, is not found into the project nor is stored as embedded file. Make sure to insert the texture as embedded file or into the project, then reimport.");
}
}
if (spatial_material.is_null()) {
// Done here so if no textures no material is created.
spatial_material.instance();
}
switch (mapping.map_mode) {
case SpatialMaterial::TextureParam::TEXTURE_METALLIC:
if (mapping.name.to_lower().find("ser") >= 0) {
// SER shader.
spatial_material->set_metallic_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_RED);
} else {
// Use grayscale as default.
spatial_material->set_metallic_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
}
break;
case SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS:
if (mapping.name.to_lower().find("ser") >= 0) {
// SER shader.
spatial_material->set_roughness_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_BLUE);
} else {
// Use grayscale as default.
spatial_material->set_roughness_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
}
break;
case SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION:
// Use grayscale as default.
spatial_material->set_ao_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
break;
case SpatialMaterial::TextureParam::TEXTURE_REFRACTION:
// Use grayscale as default.
spatial_material->set_refraction_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
break;
default:
// Nothing to do.
break;
}
spatial_material->set_texture(mapping.map_mode, texture);
}
if (spatial_material.is_valid()) {
spatial_material->set_name(material_name);
}
return spatial_material;
}

View File

@ -0,0 +1,233 @@
/*************************************************************************/
/* fbx_material.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_MATERIAL_H
#define FBX_MATERIAL_H
#include "tools/import_utils.h"
#include "core/reference.h"
#include "core/ustring.h"
struct FBXMaterial : public Reference {
String material_name = String();
mutable const FBXDocParser::Material *material = nullptr;
/* Godot materials
*** Texture Maps:
* Albedo - color, texture
* Metallic - specular, metallic, texture
* Roughness - roughness, texture
* Emission - color, texture
* Normal Map - scale, texture
* Ambient Occlusion - texture
* Refraction - scale, texture
*** Has Settings for:
* UV1 - SCALE, OFFSET
* UV2 - SCALE, OFFSET
*** Flags for
* Transparent
* Cull Mode
*/
enum class MapMode {
AlbedoM = 0,
MetallicM,
SpecularM,
EmissionM,
RoughnessM,
NormalM,
AmbientOcclusionM,
RefractionM,
ReflectionM,
};
// TODO make this static?
const std::map<std::string, SpatialMaterial::Feature> fbx_feature_mapping_desc = {
/* Transparent */
{ "TransparentColor", SpatialMaterial::Feature::FEATURE_TRANSPARENT },
{ "Maya|opacity", SpatialMaterial::Feature::FEATURE_TRANSPARENT }
};
// TODO make this static?
const std::map<std::string, SpatialMaterial::TextureParam> fbx_texture_mapping_desc = {
/* Diffuse */
{ "Maya|base", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "DiffuseColor", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "Maya|DiffuseTexture", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "Maya|baseColor", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "Maya|baseColor|file", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "3dsMax|Parameters|base_color_map", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "Maya|TEX_color_map|file", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
{ "Maya|TEX_color_map", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
/* Emission */
{ "EmissiveColor", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
{ "EmissiveFactor", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
{ "Maya|emissionColor", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
{ "Maya|emissionColor|file", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
{ "3dsMax|Parameters|emission_map", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
{ "Maya|TEX_emissive_map", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
{ "Maya|TEX_emissive_map|file", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
/* Metallic */
{ "Maya|metalness", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "Maya|metalness|file", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "3dsMax|Parameters|metalness_map", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "Maya|TEX_metallic_map", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "Maya|TEX_metallic_map|file", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "SpecularColor", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "Maya|specularColor", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "Maya|SpecularTexture", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "Maya|SpecularTexture|file", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
{ "ShininessExponent", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
/* Roughness */
{ "Maya|diffuseRoughness", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
{ "Maya|diffuseRoughness|file", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
{ "3dsMax|Parameters|roughness_map", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
{ "Maya|TEX_roughness_map", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
{ "Maya|TEX_roughness_map|file", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
{ "ReflectionFactor", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
{ "Maya|specularRoughness", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
/* Normal */
{ "NormalMap", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "Bump", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "3dsMax|Parameters|bump_map", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "Maya|NormalTexture", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "Maya|normalCamera", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "Maya|normalCamera|file", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "Maya|TEX_normal_map", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
{ "Maya|TEX_normal_map|file", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
/* AO */
{ "Maya|TEX_ao_map", SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION },
{ "Maya|TEX_ao_map|file", SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION },
// {"TransparentColor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA },
// {"TransparencyFactor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA }
};
// TODO make this static?
enum PropertyDesc {
PROPERTY_DESC_NOT_FOUND,
PROPERTY_DESC_ALBEDO_COLOR,
PROPERTY_DESC_TRANSPARENT,
PROPERTY_DESC_METALLIC,
PROPERTY_DESC_ROUGHNESS,
PROPERTY_DESC_COAT,
PROPERTY_DESC_COAT_ROUGHNESS,
PROPERTY_DESC_EMISSIVE,
PROPERTY_DESC_EMISSIVE_COLOR,
PROPERTY_DESC_IGNORE
};
const std::map<std::string, PropertyDesc> fbx_properties_desc = {
/* Albedo */
{ "DiffuseColor", PROPERTY_DESC_ALBEDO_COLOR },
{ "Maya|baseColor", PROPERTY_DESC_ALBEDO_COLOR },
/* Transparent */
{ "Opacity", PROPERTY_DESC_TRANSPARENT },
{ "TransparencyFactor", PROPERTY_DESC_TRANSPARENT },
{ "Maya|opacity", PROPERTY_DESC_TRANSPARENT },
/* Metallic */
{ "Shininess", PROPERTY_DESC_METALLIC },
{ "Reflectivity", PROPERTY_DESC_METALLIC },
{ "Maya|metalness", PROPERTY_DESC_METALLIC },
/* Roughness */
{ "Maya|diffuseRoughness", PROPERTY_DESC_ROUGHNESS },
/* Coat */
{ "Maya|coat", PROPERTY_DESC_COAT },
/* Coat roughness */
{ "Maya|coatRoughness", PROPERTY_DESC_COAT_ROUGHNESS },
/* Emissive */
{ "Maya|emission", PROPERTY_DESC_EMISSIVE },
/* Emissive color */
{ "EmissiveColor", PROPERTY_DESC_EMISSIVE_COLOR },
{ "Maya|emissionColor", PROPERTY_DESC_EMISSIVE_COLOR },
/* Ignore */
{ "Maya", PROPERTY_DESC_IGNORE },
{ "Diffuse", PROPERTY_DESC_IGNORE },
{ "Maya|TypeId", PROPERTY_DESC_IGNORE },
{ "Ambient", PROPERTY_DESC_IGNORE },
{ "AmbientColor", PROPERTY_DESC_IGNORE },
{ "ShininessExponent", PROPERTY_DESC_IGNORE },
{ "Specular", PROPERTY_DESC_IGNORE },
{ "SpecularColor", PROPERTY_DESC_IGNORE },
{ "SpecularFactor", PROPERTY_DESC_IGNORE },
//{ "BumpFactor", PROPERTY_DESC_IGNORE },
{ "Maya|exitToBackground", PROPERTY_DESC_IGNORE },
{ "Maya|indirectDiffuse", PROPERTY_DESC_IGNORE },
{ "Maya|indirectSpecular", PROPERTY_DESC_IGNORE },
{ "Maya|internalReflections", PROPERTY_DESC_IGNORE },
{ "DiffuseFactor", PROPERTY_DESC_IGNORE },
{ "AmbientFactor", PROPERTY_DESC_IGNORE },
{ "ReflectionColor", PROPERTY_DESC_IGNORE },
{ "Emissive", PROPERTY_DESC_IGNORE },
{ "Maya|coatColor", PROPERTY_DESC_IGNORE },
{ "Maya|coatNormal", PROPERTY_DESC_IGNORE },
{ "Maya|coatIOR", PROPERTY_DESC_IGNORE },
};
struct TextureFileMapping {
SpatialMaterial::TextureParam map_mode = SpatialMaterial::TEXTURE_ALBEDO;
String name = String();
const FBXDocParser::Texture *texture = nullptr;
};
/* storing the texture properties like color */
template <class T>
struct TexturePropertyMapping : Reference {
SpatialMaterial::TextureParam map_mode = SpatialMaterial::TextureParam::TEXTURE_ALBEDO;
const T property = T();
};
static void add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths);
static String find_texture_path_by_filename(const String p_filename, const String p_current_directory);
String get_material_name() const;
void set_imported_material(const FBXDocParser::Material *p_material);
struct MaterialInfo {
Vector<TextureFileMapping> textures;
Vector<SpatialMaterial::Feature> features;
};
/// Extracts the material information.
MaterialInfo extract_material_info(const FBXDocParser::Material *material) const;
Ref<SpatialMaterial> import_material(ImportState &state);
};
#endif // FBX_MATERIAL_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,175 @@
/*************************************************************************/
/* fbx_mesh_data.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_MESH_DATA_H
#define FBX_MESH_DATA_H
#include "core/hash_map.h"
#include "scene/3d/mesh_instance.h"
#include "scene/resources/surface_tool.h"
#include "fbx_bone.h"
#include "fbx_parser/FBXMeshGeometry.h"
#include "import_state.h"
#include "tools/import_utils.h"
struct FBXMeshData;
struct FBXBone;
struct ImportState;
struct VertexWeightMapping {
Vector<real_t> weights;
Vector<int> bones;
// This extra vector is used because the bone id is computed in a second step.
// TODO Get rid of this extra step is a good idea.
Vector<Ref<FBXBone> > bones_ref;
};
template <class T>
struct VertexData {
int polygon_index;
T data;
};
// Caches mesh information and instantiates meshes for you using helper functions.
struct FBXMeshData : Reference {
struct MorphVertexData {
// TODO we have only these??
/// Each element is a vertex. Not supposed to be void.
Vector<Vector3> vertices;
/// Each element is a vertex. Not supposed to be void.
Vector<Vector3> normals;
};
/// vertex id, Weight Info
/// later: perf we can use array here
HashMap<int, VertexWeightMapping> vertex_weights;
// translate fbx mesh data from document context to FBX Mesh Geometry Context
bool valid_weight_indexes = false;
MeshInstance *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *mesh_geometry, const FBXDocParser::Model *model);
void gen_weight_info(Ref<SurfaceTool> st, int vertex_id) const;
/* mesh maximum weight count */
bool valid_weight_count = false;
int max_weight_count = 0;
uint64_t armature_id = 0;
bool valid_armature_id = false;
MeshInstance *godot_mesh_instance = nullptr;
private:
void sanitize_vertex_weights();
/// Make sure to reorganize the vertices so that the correct UV is taken.
/// This step is needed because differently from the normal, that can be
/// combined, the UV may need its own triangle because sometimes they have
/// really different UV for the same vertex but different polygon.
/// This function make sure to add another vertex for those UVS.
void reorganize_vertices(
std::vector<int> &r_polygon_indices,
std::vector<Vector3> &r_vertices,
HashMap<int, Vector3> &r_normals,
HashMap<int, Vector2> &r_uv_1,
HashMap<int, Vector2> &r_uv_2,
HashMap<int, Color> &r_color,
HashMap<String, MorphVertexData> &r_morphs,
HashMap<int, HashMap<int, Vector3> > &r_normals_raw,
HashMap<int, HashMap<int, Vector2> > &r_uv_1_raw,
HashMap<int, HashMap<int, Vector2> > &r_uv_2_raw);
void add_vertex(
Ref<SurfaceTool> p_surface_tool,
real_t p_scale,
int p_vertex,
const std::vector<Vector3> &p_vertices_position,
const HashMap<int, Vector3> &p_normals,
const HashMap<int, Vector2> &p_uvs_0,
const HashMap<int, Vector2> &p_uvs_1,
const HashMap<int, Color> &p_colors,
const Vector3 &p_morph_value = Vector3(),
const Vector3 &p_morph_normal = Vector3());
void triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, Vector<int> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const;
/// This function is responsible to convert the FBX polygon vertex to
/// vertex index.
/// The polygon vertices are stored in an array with some negative
/// values. The negative values define the last face index.
/// For example the following `face_array` contains two faces, the former
/// with 3 vertices and the latter with a line:
/// [0,2,-2,3,-5]
/// Parsed as:
/// [0, 2, 1, 3, 4]
/// The negative values are computed using this formula: `(-value) - 1`
///
/// Returns the vertex index from the poligon vertex.
/// Returns -1 if `p_index` is invalid.
int get_vertex_from_polygon_vertex(const std::vector<int> &p_face_indices, int p_index) const;
/// Retuns true if this polygon_vertex_index is the end of a new polygon.
bool is_end_of_polygon(const std::vector<int> &p_face_indices, int p_index) const;
/// Retuns true if this polygon_vertex_index is the begin of a new polygon.
bool is_start_of_polygon(const std::vector<int> &p_face_indices, int p_index) const;
/// Returns the number of polygons.
int count_polygons(const std::vector<int> &p_face_indices) const;
/// Used to extract data from the `MappingData` alligned with vertex.
/// Useful to extract normal/uvs/colors/tangets/etc...
/// If the function fails somehow, it returns an hollow vector and print an error.
template <class R, class T>
HashMap<int, R> extract_per_vertex_data(
int p_vertex_count,
const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edges,
const std::vector<int> &p_mesh_indices,
const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data,
R (*collector_function)(const Vector<VertexData<T> > *p_vertex_data, R p_fall_back),
R p_fall_back) const;
/// Used to extract data from the `MappingData` organized per polygon.
/// Useful to extract the materila
/// If the function fails somehow, it returns an hollow vector and print an error.
template <class T>
HashMap<int, T> extract_per_polygon(
int p_vertex_count,
const std::vector<int> &p_face_indices,
const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data,
T p_fallback_value) const;
/// Extracts the morph data and organizes it per vertices.
/// The returned `MorphVertexData` arrays are never something different
/// then the `vertex_count`.
void extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data);
};
#endif // FBX_MESH_DATA_H

View File

@ -0,0 +1,63 @@
/*************************************************************************/
/* fbx_node.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_NODE_H
#define FBX_NODE_H
#include "fbx_skeleton.h"
#include "model_abstraction.h"
#include "pivot_transform.h"
#include "fbx_parser/FBXDocument.h"
class Spatial;
struct PivotTransform;
struct FBXNode : Reference, ModelAbstraction {
uint64_t current_node_id = 0;
String node_name = String();
Spatial *godot_node = nullptr;
// used to parent the skeleton once the tree is built.
Ref<FBXSkeleton> skeleton_node = Ref<FBXSkeleton>();
void set_parent(Ref<FBXNode> p_parent) {
fbx_parent = p_parent;
}
void set_pivot_transform(Ref<PivotTransform> p_pivot_transform) {
pivot_transform = p_pivot_transform;
}
Ref<PivotTransform> pivot_transform = Ref<PivotTransform>(); // local and global xform data
Ref<FBXNode> fbx_parent = Ref<FBXNode>(); // parent node
};
#endif // FBX_NODE_H

View File

@ -0,0 +1,124 @@
/*************************************************************************/
/* fbx_skeleton.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "fbx_skeleton.h"
#include "import_state.h"
#include "tools/import_utils.h"
void FBXSkeleton::init_skeleton(const ImportState &state) {
int skeleton_bone_count = skeleton_bones.size();
if (skeleton == nullptr && skeleton_bone_count > 0) {
skeleton = memnew(Skeleton);
Ref<FBXNode> skeleton_parent_node;
if (fbx_node.is_valid()) {
// cache skeleton attachment for later during node creation
// can't be done until after node hierarchy is built
if (fbx_node->godot_node != state.root) {
fbx_node->skeleton_node = Ref<FBXSkeleton>(this);
print_verbose("cached armature skeleton attachment for node " + fbx_node->node_name);
} else {
// root node must never be a skeleton to prevent cyclic skeletons from being allowed (skeleton in a skeleton)
fbx_node->godot_node->add_child(skeleton);
skeleton->set_owner(state.root_owner);
skeleton->set_name("Skeleton");
print_verbose("created armature skeleton for root");
}
} else {
memfree(skeleton);
skeleton = nullptr;
print_error("[doc] skeleton has no valid node to parent nodes to - erasing");
skeleton_bones.clear();
return;
}
}
// Make the bone name uniques.
for (int x = 0; x < skeleton_bone_count; x++) {
Ref<FBXBone> bone = skeleton_bones[x];
if (bone.is_valid()) {
// Make sure the bone name is unique.
const String bone_name = bone->bone_name;
int same_name_count = 0;
for (int y = x; y < skeleton_bone_count; y++) {
Ref<FBXBone> other_bone = skeleton_bones[y];
if (other_bone.is_valid()) {
if (other_bone->bone_name == bone_name) {
same_name_count += 1;
other_bone->bone_name += "_" + itos(same_name_count);
}
}
}
}
}
Map<int, Ref<FBXBone> > bone_map;
// implement fbx cluster skin logic here this is where it goes
int bone_count = 0;
for (int x = 0; x < skeleton_bone_count; x++) {
Ref<FBXBone> bone = skeleton_bones[x];
if (bone.is_valid()) {
skeleton->add_bone(bone->bone_name);
bone->godot_bone_id = bone_count;
bone->fbx_skeleton = Ref<FBXSkeleton>(this);
bone_map.insert(bone_count, bone);
print_verbose("added bone " + itos(bone->bone_id) + " " + bone->bone_name);
bone_count++;
}
}
ERR_FAIL_COND_MSG(skeleton->get_bone_count() != bone_count, "Not all bones got added, is the file corrupted?");
for (Map<int, Ref<FBXBone> >::Element *bone_element = bone_map.front(); bone_element; bone_element = bone_element->next()) {
Ref<FBXBone> bone = bone_element->value();
int bone_index = bone_element->key();
print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name);
skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->pivot_xform->LocalTransform, state.scale));
// lookup parent ID
if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) {
Ref<FBXBone> parent_bone = state.fbx_bone_map[bone->parent_bone_id];
int bone_id = skeleton->find_bone(parent_bone->bone_name);
if (bone_id != -1) {
skeleton->set_bone_parent(bone_index, bone_id);
} else {
print_error("invalid bone parent: " + parent_bone->bone_name);
}
} else {
if (bone->godot_bone_id != -1) {
skeleton->set_bone_parent(bone_index, -1); // no parent for this bone
}
}
}
}

View File

@ -0,0 +1,53 @@
/*************************************************************************/
/* fbx_skeleton.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_SKELETON_H
#define FBX_SKELETON_H
#include "fbx_bone.h"
#include "fbx_node.h"
#include "model_abstraction.h"
#include "core/reference.h"
#include "scene/3d/skeleton.h"
struct FBXNode;
struct ImportState;
struct FBXBone;
struct FBXSkeleton : Reference, ModelAbstraction {
Ref<FBXNode> fbx_node = Ref<FBXNode>();
Vector<Ref<FBXBone> > skeleton_bones = Vector<Ref<FBXBone> >();
Skeleton *skeleton = nullptr;
void init_skeleton(const ImportState &state);
};
#endif // FBX_SKELETON_H

View File

@ -28,8 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EDITOR_SCENE_IMPORT_STATE_H
#define EDITOR_SCENE_IMPORT_STATE_H
#ifndef IMPORT_STATE_H
#define IMPORT_STATE_H
#include "fbx_mesh_data.h"
#include "modules/fbx/tools/import_utils.h"
#include "pivot_transform.h"
#include "core/bind/core_bind.h"
#include "core/io/resource_importer.h"
@ -43,91 +47,64 @@
#include "scene/resources/animation.h"
#include "scene/resources/surface_tool.h"
#include <assimp/matrix4x4.h>
#include <assimp/scene.h>
#include <assimp/types.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/LogStream.hpp>
#include <assimp/Logger.hpp>
#include "modules/fbx/fbx_parser/FBXDocument.h"
#include "modules/fbx/fbx_parser/FBXImportSettings.h"
#include "modules/fbx/fbx_parser/FBXMeshGeometry.h"
#include "modules/fbx/fbx_parser/FBXParser.h"
#include "modules/fbx/fbx_parser/FBXTokenizer.h"
#include "modules/fbx/fbx_parser/FBXUtil.h"
struct FBXBone;
struct FBXMeshData;
struct FBXNode;
struct FBXSkeleton;
namespace AssimpImporter {
/** Import state is for global scene import data
* This makes the code simpler and contains useful lookups.
*/
struct ImportState {
bool enable_material_import = true;
bool enable_animation_import = true;
String path;
Spatial *root;
const aiScene *assimp_scene;
uint32_t max_bone_weights;
Map<StringName, Ref<Texture> > cached_image_searches;
Map<uint64_t, Ref<SpatialMaterial> > cached_materials;
Map<String, Ref<Mesh> > mesh_cache;
Map<int, Ref<Material> > material_cache;
Map<String, int> light_cache;
Map<String, int> camera_cache;
String path = String();
Spatial *root_owner = nullptr;
Spatial *root = nullptr;
real_t scale = 0.01;
Ref<FBXNode> fbx_root_node = Ref<FBXNode>();
// skeleton map - merged automatically when they are on the same x node in the tree so we can merge them automatically.
Map<uint64_t, Ref<FBXSkeleton> > skeleton_map = Map<uint64_t, Ref<FBXSkeleton> >();
// very useful for when you need to ask assimp for the bone mesh
// nodes on the same level get merged automatically.
//Map<uint64_t, Skeleton *> armature_map;
AnimationPlayer *animation_player = nullptr;
Map<const aiNode *, Node *> assimp_node_map;
Map<String, Ref<Image> > path_to_image_cache;
// Generation 4 - Raw document accessing for bone/skin/joint/kLocators
// joints are not necessarily bones but must be merged into the skeleton
// (bone id), bone
Map<uint64_t, Ref<FBXBone> > fbx_bone_map = Map<uint64_t, Ref<FBXBone> >(); // this is the bone name and setup information required for joints
// this will never contain joints only bones attached to a mesh.
// Generation 3 - determinisitic iteration
// to lower potential recursion errors
List<const aiNode *> nodes;
Map<const aiNode *, Spatial *> flat_node_map;
AnimationPlayer *animation_player;
// Generation 4 - Raw document for creating the nodes transforms in the scene
// this is a list of the nodes in the scene
// (id, node)
List<Ref<FBXNode> > fbx_node_list = List<Ref<FBXNode> >();
// Generation 3 - deterministic armatures
// list of armature nodes - flat and simple to parse
// assimp node, node in godot
List<aiNode *> armature_nodes;
Map<const aiNode *, Skeleton *> armature_skeletons;
Map<aiBone *, Skeleton *> skeleton_bone_map;
// Generation 3 - deterministic bone handling
// bones from the stack are popped when found
// this means we can detect
// what bones are for other armatures
List<aiBone *> bone_stack;
// All nodes which have been created in the scene
// this will not contain the root node of the scene
Map<uint64_t, Ref<FBXNode> > fbx_target_map = Map<uint64_t, Ref<FBXNode> >();
// EditorSceneImporter::ImportFlags
uint32_t import_flags;
// mesh nodes which are created in node / mesh step - used for populating skin poses in MeshSkins
Map<uint64_t, Ref<FBXNode> > MeshNodes = Map<uint64_t, Ref<FBXNode> >();
// mesh skin map
Map<uint64_t, Ref<Skin> > MeshSkins = Map<uint64_t, Ref<Skin> >();
// this is the container for the mesh weight information and eventually
// any mesh data
// but not the skin, just stuff important for rendering
// skin is applied to mesh instance so not really required to be in here yet.
// maybe later
// fbx mesh id, FBXMeshData
Map<uint64_t, Ref<FBXMeshData> > renderer_mesh_data = Map<uint64_t, Ref<FBXMeshData> >();
};
struct AssimpImageData {
Ref<Image> raw_image;
Ref<ImageTexture> texture;
aiTextureMapMode map_mode[2];
};
/** Recursive state is used to push state into functions instead of specifying them
* This makes the code easier to handle too and add extra arguments without breaking things
*/
struct RecursiveState {
RecursiveState() {} // do not construct :)
RecursiveState(
Transform &_node_transform,
Skeleton *_skeleton,
Spatial *_new_node,
String &_node_name,
aiNode *_assimp_node,
Node *_parent_node,
aiBone *_bone) :
node_transform(_node_transform),
skeleton(_skeleton),
new_node(_new_node),
node_name(_node_name),
assimp_node(_assimp_node),
parent_node(_parent_node),
bone(_bone) {}
Transform node_transform;
Skeleton *skeleton = NULL;
Spatial *new_node = NULL;
String node_name;
aiNode *assimp_node = NULL;
Node *parent_node = NULL;
aiBone *bone = NULL;
};
} // namespace AssimpImporter
#endif // EDITOR_SCENE_IMPORT_STATE_H
#endif // IMPORT_STATE_H

View File

@ -0,0 +1,52 @@
/*************************************************************************/
/* model_abstraction.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MODEL_ABSTRACTION_H
#define MODEL_ABSTRACTION_H
#include "modules/fbx/fbx_parser/FBXDocument.h"
struct ModelAbstraction {
mutable const FBXDocParser::Model *fbx_model = nullptr;
void set_model(const FBXDocParser::Model *p_model) {
fbx_model = p_model;
}
bool has_model() const {
return fbx_model != nullptr;
}
const FBXDocParser::Model *get_model() const {
return fbx_model;
}
};
#endif // MODEL_ABSTRACTION_H

View File

@ -0,0 +1,257 @@
/*************************************************************************/
/* pivot_transform.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "pivot_transform.h"
#include "tools/import_utils.h"
void PivotTransform::ReadTransformChain() {
const FBXDocParser::PropertyTable *props = fbx_model->Props();
const FBXDocParser::Model::RotOrder &rot = fbx_model->RotationOrder();
const FBXDocParser::TransformInheritance &inheritType = fbx_model->InheritType();
inherit_type = inheritType; // copy the inherit type we need it in the second step.
print_verbose("Model: " + String(fbx_model->Name().c_str()) + " Has inherit type: " + itos(fbx_model->InheritType()));
bool ok = false;
raw_pre_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", ok));
if (ok) {
pre_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_pre_rotation));
print_verbose("valid pre_rotation: " + raw_pre_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI)));
}
raw_post_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", ok));
if (ok) {
post_rotation = ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder_EulerXYZ, ImportUtils::deg2rad(raw_post_rotation));
print_verbose("valid post_rotation: " + raw_post_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI)));
}
const Vector3 &RotationPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationPivot", ok));
if (ok) {
rotation_pivot = ImportUtils::FixAxisConversions(RotationPivot);
}
const Vector3 &RotationOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationOffset", ok));
if (ok) {
rotation_offset = ImportUtils::FixAxisConversions(RotationOffset);
}
const Vector3 &ScalingOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingOffset", ok));
if (ok) {
scaling_offset = ImportUtils::FixAxisConversions(ScalingOffset);
}
const Vector3 &ScalingPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingPivot", ok));
if (ok) {
scaling_pivot = ImportUtils::FixAxisConversions(ScalingPivot);
}
const Vector3 &Translation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Translation", ok));
if (ok) {
translation = ImportUtils::FixAxisConversions(Translation);
}
raw_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Rotation", ok));
if (ok) {
rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_rotation));
}
const Vector3 &Scaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Scaling", ok));
if (ok) {
scaling = Scaling;
}
const Vector3 &GeometricScaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricScaling", ok));
if (ok) {
geometric_scaling = GeometricScaling;
}
const Vector3 &GeometricRotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricRotation", ok));
if (ok) {
geometric_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(GeometricRotation));
}
const Vector3 &GeometricTranslation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricTranslation", ok));
if (ok) {
geometric_translation = ImportUtils::FixAxisConversions(GeometricTranslation);
}
}
Transform PivotTransform::ComputeLocalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const {
Transform T, Roff, Rp, Soff, Sp, S;
// Here I assume this is the operation which needs done.
// Its WorldTransform * V
// Origin pivots
T.set_origin(p_translation);
Roff.set_origin(rotation_offset);
Rp.set_origin(rotation_pivot);
Soff.set_origin(scaling_offset);
Sp.set_origin(scaling_pivot);
// Scaling node
S.scale(p_scaling);
// Rotation pivots
Transform Rpre = Transform(pre_rotation);
Transform R = Transform(p_rotation);
Transform Rpost = Transform(post_rotation);
return T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
}
Transform PivotTransform::ComputeGlobalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const {
Transform T, Roff, Rp, Soff, Sp, S;
// Here I assume this is the operation which needs done.
// Its WorldTransform * V
// Origin pivots
T.set_origin(p_translation);
Roff.set_origin(rotation_offset);
Rp.set_origin(rotation_pivot);
Soff.set_origin(scaling_offset);
Sp.set_origin(scaling_pivot);
// Scaling node
S.scale(p_scaling);
// Rotation pivots
Transform Rpre = Transform(pre_rotation);
Transform R = Transform(p_rotation);
Transform Rpost = Transform(post_rotation);
Transform parent_global_xform;
Transform parent_local_scaling_m;
if (parent_transform.is_valid()) {
parent_global_xform = parent_transform->GlobalTransform;
parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
}
Transform local_rotation_m, parent_global_rotation_m;
Quat parent_global_rotation = parent_global_xform.basis.get_rotation_quat();
parent_global_rotation_m.basis.set_quat(parent_global_rotation);
local_rotation_m = Rpre * R * Rpost;
//Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quat().normalized());
Transform local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation;
Vector3 parent_translation = parent_global_xform.get_origin();
parent_shear_translation.origin = parent_translation;
parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform;
parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation;
local_shear_scaling = S;
// Inherit type handler - we don't care about T here, just reordering RSrs etc.
Transform global_rotation_scale;
if (inherit_type == FBXDocParser::Transform_RrSs) {
global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling;
} else if (inherit_type == FBXDocParser::Transform_RSrs) {
global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling;
} else if (inherit_type == FBXDocParser::Transform_Rrs) {
Transform parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.affine_inverse();
global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling;
}
Transform local_transform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
//Transform local_translation_pivoted = Transform(Basis(), LocalTransform.origin);
// manual hack to force SSC not to be compensated for - until we can handle it properly with tests
return parent_global_xform * local_transform;
}
void PivotTransform::ComputePivotTransform() {
Transform T, Roff, Rp, Soff, Sp, S;
// Here I assume this is the operation which needs done.
// Its WorldTransform * V
// Origin pivots
T.set_origin(translation);
Roff.set_origin(rotation_offset);
Rp.set_origin(rotation_pivot);
Soff.set_origin(scaling_offset);
Sp.set_origin(scaling_pivot);
// Scaling node
if (!scaling.is_equal_approx(Vector3())) {
S.scale(scaling);
} else {
S.scale(Vector3(1, 1, 1));
}
Local_Scaling_Matrix = S; // copy for when node / child is looking for the value of this.
// Rotation pivots
Transform Rpre = Transform(pre_rotation);
Transform R = Transform(rotation);
Transform Rpost = Transform(post_rotation);
Transform parent_global_xform;
Transform parent_local_scaling_m;
if (parent_transform.is_valid()) {
parent_global_xform = parent_transform->GlobalTransform;
parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
}
Transform local_rotation_m, parent_global_rotation_m;
Quat parent_global_rotation = parent_global_xform.basis.get_rotation_quat();
parent_global_rotation_m.basis.set_quat(parent_global_rotation);
local_rotation_m = Rpre * R * Rpost;
//Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quat().normalized());
Transform local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation;
Vector3 parent_translation = parent_global_xform.get_origin();
parent_shear_translation.origin = parent_translation;
parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform;
parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation;
local_shear_scaling = S;
// Inherit type handler - we don't care about T here, just reordering RSrs etc.
Transform global_rotation_scale;
if (inherit_type == FBXDocParser::Transform_RrSs) {
global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling;
} else if (inherit_type == FBXDocParser::Transform_RSrs) {
global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling;
} else if (inherit_type == FBXDocParser::Transform_Rrs) {
Transform parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.inverse();
global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling;
}
LocalTransform = Transform();
LocalTransform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
ERR_FAIL_COND_MSG(LocalTransform.basis.determinant() == 0, "invalid scale reset");
Transform local_translation_pivoted = Transform(Basis(), LocalTransform.origin);
GlobalTransform = Transform();
//GlobalTransform = parent_global_xform * LocalTransform;
Transform global_origin = Transform(Basis(), parent_translation);
GlobalTransform = (global_origin * local_translation_pivoted) * global_rotation_scale;
ImportUtils::debug_xform("local xform calculation", LocalTransform);
print_verbose("scale of node: " + S.basis.get_scale_local());
print_verbose("---------------------------------------------------------------");
}
void PivotTransform::Execute() {
ReadTransformChain();
ComputePivotTransform();
ImportUtils::debug_xform("global xform: ", GlobalTransform);
computed_global_xform = true;
}

View File

@ -0,0 +1,112 @@
/*************************************************************************/
/* pivot_transform.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef PIVOT_TRANSFORM_H
#define PIVOT_TRANSFORM_H
#include "core/reference.h"
#include "model_abstraction.h"
#include "fbx_parser/FBXDocument.h"
#include "tools/import_utils.h"
enum TransformationComp {
TransformationComp_Translation,
TransformationComp_Scaling,
TransformationComp_Rotation,
TransformationComp_RotationOffset,
TransformationComp_RotationPivot,
TransformationComp_PreRotation,
TransformationComp_PostRotation,
TransformationComp_ScalingOffset,
TransformationComp_ScalingPivot,
TransformationComp_GeometricTranslation,
TransformationComp_GeometricRotation,
TransformationComp_GeometricScaling,
TransformationComp_MAXIMUM
};
// Abstract away pivot data so its simpler to handle
struct PivotTransform : Reference, ModelAbstraction {
// at the end we want to keep geometric_ everything, post and pre rotation
// these are used during animation data processing / keyframe ingestion the rest can be simplified down / out.
Quat pre_rotation = Quat();
Quat post_rotation = Quat();
Quat rotation = Quat();
Quat geometric_rotation = Quat();
Vector3 rotation_pivot = Vector3();
Vector3 rotation_offset = Vector3();
Vector3 scaling_offset = Vector3(1.0, 1.0, 1.0);
Vector3 scaling_pivot = Vector3(1.0, 1.0, 1.0);
Vector3 translation = Vector3();
Vector3 scaling = Vector3(1.0, 1.0, 1.0);
Vector3 geometric_scaling = Vector3(1.0, 1.0, 1.0);
Vector3 geometric_translation = Vector3();
Vector3 raw_rotation = Vector3();
Vector3 raw_post_rotation = Vector3();
Vector3 raw_pre_rotation = Vector3();
/* Read pivots from the document */
void ReadTransformChain();
void debug_pivot_xform(String p_name) {
print_verbose("debugging node name: " + p_name);
print_verbose("raw rotation: " + raw_rotation * (180 / Math_PI));
print_verbose("raw pre_rotation " + raw_pre_rotation * (180 / Math_PI));
print_verbose("raw post_rotation " + raw_post_rotation * (180 / Math_PI));
}
Transform ComputeGlobalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const;
Transform ComputeLocalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const;
/* Extract into xforms and calculate once */
void ComputePivotTransform();
/* Execute the command for the pivot generation */
void Execute();
void set_parent(Ref<PivotTransform> p_parent) {
parent_transform = p_parent;
}
bool computed_global_xform = false;
Ref<PivotTransform> parent_transform = Ref<PivotTransform>();
//Transform chain[TransformationComp_MAXIMUM];
// cached for later use
Transform GlobalTransform = Transform();
Transform LocalTransform = Transform();
Transform Local_Scaling_Matrix = Transform(); // used for inherit type.
Transform GeometricTransform = Transform(); // 3DS max only
FBXDocParser::TransformInheritance inherit_type = FBXDocParser::TransformInheritance_MAX; // maya fbx requires this - sorry <3
};
#endif // PIVOT_TRANSFORM_H

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*************************************************************************/
/* editor_scene_importer_assimp.h */
/* editor_scene_importer_fbx.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,10 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EDITOR_SCENE_IMPORTER_ASSIMP_H
#define EDITOR_SCENE_IMPORTER_ASSIMP_H
#ifndef EDITOR_SCENE_IMPORTER_FBX_H
#define EDITOR_SCENE_IMPORTER_FBX_H
#ifdef TOOLS_ENABLED
#include "data/import_state.h"
#include "tools/import_utils.h"
#include "core/bind/core_bind.h"
#include "core/io/resource_importer.h"
#include "core/vector.h"
@ -44,35 +48,16 @@
#include "scene/resources/animation.h"
#include "scene/resources/surface_tool.h"
#include <assimp/matrix4x4.h>
#include <assimp/scene.h>
#include <assimp/types.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/LogStream.hpp>
#include <assimp/Logger.hpp>
#include <map>
#include "fbx_parser/FBXDocument.h"
#include "fbx_parser/FBXImportSettings.h"
#include "fbx_parser/FBXMeshGeometry.h"
#include "fbx_parser/FBXUtil.h"
#include "import_state.h"
#include "import_utils.h"
#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
using namespace AssimpImporter;
class AssimpStream : public Assimp::LogStream {
public:
// Constructor
AssimpStream() {}
// Destructor
~AssimpStream() {}
// Write something using your own functionality
void write(const char *message) {
print_verbose(String("Open Asset Import: ") + String(message).strip_edges());
}
};
class EditorSceneImporterAssimp : public EditorSceneImporter {
class EditorSceneImporterFBX : public EditorSceneImporter {
private:
GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
GDCLASS(EditorSceneImporterFBX, EditorSceneImporter);
struct AssetImportAnimation {
enum Interpolation {
@ -83,67 +68,65 @@ private:
};
};
struct BoneInfo {
uint32_t bone;
float weight;
};
// ------------------------------------------------------------------------------------------------
template <typename T>
const T *ProcessDOMConnection(
const FBXDocParser::Document *doc,
uint64_t current_element,
bool reverse_lookup = false) {
Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
const aiNode *assimp_node, Ref<Skin> &skin,
Skeleton *&skeleton_assigned);
const std::vector<const FBXDocParser::Connection *> &conns = reverse_lookup ? doc->GetConnectionsByDestinationSequenced(current_element) : doc->GetConnectionsBySourceSequenced(current_element);
//print_verbose("[doc] looking for " + String(element_to_find));
// using the temp pattern here so we can debug before it returns
// in some cases we return too early, with 'deformer object base class' in wrong place
// in assimp this means we can accidentally return too early...
const T *return_obj = nullptr;
// simple object creation functions
Spatial *create_light(ImportState &state,
const String &node_name,
Transform &look_at_transform);
Spatial *create_camera(
ImportState &state,
const String &node_name,
Transform &look_at_transform);
// non recursive - linear so must not use recursive arguments
MeshInstance *create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform);
// recursive node generator
void _generate_node(ImportState &state, const aiNode *assimp_node);
void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
int anim_fps, Ref<Animation> animation, float ticks_per_second,
Skeleton *skeleton, const NodePath &node_path,
const String &node_name, aiBone *track_bone);
for (const FBXDocParser::Connection *con : conns) {
const FBXDocParser::Object *source_object = con->SourceObject();
const FBXDocParser::Object *dest_object = con->DestinationObject();
if (source_object && dest_object != nullptr) {
//print_verbose("[doc] connection name: " + String(source_object->Name().c_str()) + ", dest: " + String(dest_object->Name().c_str()));
const T *temp = dynamic_cast<const T *>(reverse_lookup ? source_object : dest_object);
if (temp) {
return_obj = temp;
}
}
}
void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
Node *get_node_by_name(ImportState &state, String name);
aiBone *get_bone_from_stack(ImportState &state, aiString name);
Spatial *_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
if (return_obj != nullptr) {
//print_verbose("[doc] returned valid element");
//print_verbose("Found object for bone");
return return_obj;
}
// safe to return nothing, need to use nullptr here as nullptr is used internally for FBX document.
return nullptr;
}
void BuildDocumentBones(Ref<FBXBone> p_parent_bone,
ImportState &state, const FBXDocParser::Document *p_doc,
uint64_t p_id);
void BuildDocumentNodes(Ref<PivotTransform> parent_transform, ImportState &state, const FBXDocParser::Document *doc, uint64_t id, Ref<FBXNode> fbx_parent);
Spatial *_generate_scene(const String &p_path, const FBXDocParser::Document *p_document,
const uint32_t p_flags,
int p_bake_fps, const int32_t p_max_bone_weights);
template <class T>
T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const;
struct ImportFormat {
Vector<String> extensions;
bool is_default;
};
protected:
static void _bind_methods();
public:
EditorSceneImporterAssimp() {
Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE);
unsigned int severity = Assimp::Logger::Info | Assimp::Logger::Err | Assimp::Logger::Warn;
Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), severity);
}
~EditorSceneImporterAssimp() {
Assimp::DefaultLogger::kill();
}
EditorSceneImporterFBX() {}
~EditorSceneImporterFBX() {}
virtual void get_extensions(List<String> *r_extensions) const;
virtual uint32_t get_import_flags() const;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL);
Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
static void RegenerateBoneStack(ImportState &state);
void RegenerateBoneStack(ImportState &state, aiMesh *mesh);
void create_mesh_data_skin(ImportState &state, const Ref<FBXNode> &fbx_node, uint64_t mesh_id);
};
#endif
#endif
#endif // TOOLS_ENABLED
#endif // EDITOR_SCENE_IMPORTER_FBX_H

View File

@ -0,0 +1,283 @@
/*************************************************************************/
/* ByteSwapper.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2020, 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 Helper class tp perform various byte oder swappings
(e.g. little to big endian) */
#ifndef BYTE_SWAPPER_H
#define BYTE_SWAPPER_H
#include <stdint.h>
#include <algorithm>
#include <locale>
namespace FBXDocParser {
// --------------------------------------------------------------------------------------
/** Defines some useful byte order swap routines.
*
* This is required to read big-endian model formats on little-endian machines,
* and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */
// --------------------------------------------------------------------------------------
class ByteSwap {
ByteSwap() {}
public:
// ----------------------------------------------------------------------
/** Swap two bytes of data
* @param[inout] _szOut A void* to save the reintcasts for the caller. */
static inline void Swap2(void *_szOut) {
uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
std::swap(szOut[0], szOut[1]);
}
// ----------------------------------------------------------------------
/** Swap four bytes of data
* @param[inout] _szOut A void* to save the reintcasts for the caller. */
static inline void Swap4(void *_szOut) {
uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
std::swap(szOut[0], szOut[3]);
std::swap(szOut[1], szOut[2]);
}
// ----------------------------------------------------------------------
/** Swap eight bytes of data
* @param[inout] _szOut A void* to save the reintcasts for the caller. */
static inline void Swap8(void *_szOut) {
uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
std::swap(szOut[0], szOut[7]);
std::swap(szOut[1], szOut[6]);
std::swap(szOut[2], szOut[5]);
std::swap(szOut[3], szOut[4]);
}
// ----------------------------------------------------------------------
/** ByteSwap a float. Not a joke.
* @param[inout] fOut ehm. .. */
static inline void Swap(float *fOut) {
Swap4(fOut);
}
// ----------------------------------------------------------------------
/** ByteSwap a double. Not a joke.
* @param[inout] fOut ehm. .. */
static inline void Swap(double *fOut) {
Swap8(fOut);
}
// ----------------------------------------------------------------------
/** ByteSwap an int16t. Not a joke.
* @param[inout] fOut ehm. .. */
static inline void Swap(int16_t *fOut) {
Swap2(fOut);
}
static inline void Swap(uint16_t *fOut) {
Swap2(fOut);
}
// ----------------------------------------------------------------------
/** ByteSwap an int32t. Not a joke.
* @param[inout] fOut ehm. .. */
static inline void Swap(int32_t *fOut) {
Swap4(fOut);
}
static inline void Swap(uint32_t *fOut) {
Swap4(fOut);
}
// ----------------------------------------------------------------------
/** ByteSwap an int64t. Not a joke.
* @param[inout] fOut ehm. .. */
static inline void Swap(int64_t *fOut) {
Swap8(fOut);
}
static inline void Swap(uint64_t *fOut) {
Swap8(fOut);
}
// ----------------------------------------------------------------------
//! Templatized ByteSwap
//! \returns param tOut as swapped
template <typename Type>
static inline Type Swapped(Type tOut) {
return _swapper<Type, sizeof(Type)>()(tOut);
}
private:
template <typename T, size_t size>
struct _swapper;
};
template <typename T>
struct ByteSwap::_swapper<T, 2> {
T operator()(T tOut) {
Swap2(&tOut);
return tOut;
}
};
template <typename T>
struct ByteSwap::_swapper<T, 4> {
T operator()(T tOut) {
Swap4(&tOut);
return tOut;
}
};
template <typename T>
struct ByteSwap::_swapper<T, 8> {
T operator()(T tOut) {
Swap8(&tOut);
return tOut;
}
};
// --------------------------------------------------------------------------------------
// ByteSwap macros for BigEndian/LittleEndian support
// --------------------------------------------------------------------------------------
#if (defined AI_BUILD_BIG_ENDIAN)
#define AI_LE(t) (t)
#define AI_BE(t) ByteSwap::Swapped(t)
#define AI_LSWAP2(p)
#define AI_LSWAP4(p)
#define AI_LSWAP8(p)
#define AI_LSWAP2P(p)
#define AI_LSWAP4P(p)
#define AI_LSWAP8P(p)
#define LE_NCONST const
#define AI_SWAP2(p) ByteSwap::Swap2(&(p))
#define AI_SWAP4(p) ByteSwap::Swap4(&(p))
#define AI_SWAP8(p) ByteSwap::Swap8(&(p))
#define AI_SWAP2P(p) ByteSwap::Swap2((p))
#define AI_SWAP4P(p) ByteSwap::Swap4((p))
#define AI_SWAP8P(p) ByteSwap::Swap8((p))
#define BE_NCONST
#else
#define AI_BE(t) (t)
#define AI_LE(t) ByteSwap::Swapped(t)
#define AI_SWAP2(p)
#define AI_SWAP4(p)
#define AI_SWAP8(p)
#define AI_SWAP2P(p)
#define AI_SWAP4P(p)
#define AI_SWAP8P(p)
#define BE_NCONST const
#define AI_LSWAP2(p) ByteSwap::Swap2(&(p))
#define AI_LSWAP4(p) ByteSwap::Swap4(&(p))
#define AI_LSWAP8(p) ByteSwap::Swap8(&(p))
#define AI_LSWAP2P(p) ByteSwap::Swap2((p))
#define AI_LSWAP4P(p) ByteSwap::Swap4((p))
#define AI_LSWAP8P(p) ByteSwap::Swap8((p))
#define LE_NCONST
#endif
namespace Intern {
// --------------------------------------------------------------------------------------------
template <typename T, bool doit>
struct ByteSwapper {
void operator()(T *inout) {
ByteSwap::Swap(inout);
}
};
template <typename T>
struct ByteSwapper<T, false> {
void operator()(T *) {
}
};
// --------------------------------------------------------------------------------------------
template <bool SwapEndianess, typename T, bool RuntimeSwitch>
struct Getter {
void operator()(T *inout, bool le) {
le = !le;
if (le) {
ByteSwapper<T, (sizeof(T) > 1 ? true : false)>()(inout);
} else
ByteSwapper<T, false>()(inout);
}
};
template <bool SwapEndianess, typename T>
struct Getter<SwapEndianess, T, false> {
void operator()(T *inout, bool /*le*/) {
// static branch
ByteSwapper<T, (SwapEndianess && sizeof(T) > 1)>()(inout);
}
};
} // namespace Intern
} // namespace FBXDocParser
#endif // BYTE_SWAPPER_H

View File

@ -60,7 +60,7 @@ The GUY who performed some of the CSM mocaps.
Contributed fixes for the documentation and the doxygen markup
- Zhao Lei
Contributed several bugfixes fixing memory leaks and improving float parsing
Contributed several bugfixes fixing memory leaks and improving float parsing
- sueastside
Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
@ -129,7 +129,7 @@ Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
Contributed the Debian build fixes ( architecture macro ).
- gellule
Several LWO and LWS fixes (pivoting).
Several LWO and LWS fixes (pivoting).
- Marcel Metz
GCC/Linux fixes for the SimpleOpenGL sample.

View File

@ -0,0 +1,294 @@
/*************************************************************************/
/* FBXAnimation.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXAnimation.cpp
* @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode,
* Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack
*/
#include "FBXCommon.h"
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXParser.h"
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
AnimationCurve::AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document & /*doc*/) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr KeyTime = GetRequiredElement(sc, "KeyTime");
const ElementPtr KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat");
// note preserved keys and values for legacy FBXConverter.cpp
// we can remove this once the animation system is written
// and clean up this code so we do not have copies everywhere.
ParseVectorDataArray(keys, KeyTime);
ParseVectorDataArray(values, KeyValueFloat);
if (keys.size() != values.size()) {
DOMError("the number of key times does not match the number of keyframe values", KeyTime);
}
// put the two lists into the map, underlying container is really just a dictionary
// these will always match, if not an error will throw and the file will not import
// this is useful because we then can report something and fix this later if it becomes an issue
// at this point we do not need a different count of these elements so this makes the
// most sense to do.
for (size_t x = 0; x < keys.size(); x++) {
keyvalues[keys[x]] = values[x];
}
const ElementPtr KeyAttrDataFloat = sc->GetElement("KeyAttrDataFloat");
if (KeyAttrDataFloat) {
ParseVectorDataArray(attributes, KeyAttrDataFloat);
}
const ElementPtr KeyAttrFlags = sc->GetElement("KeyAttrFlags");
if (KeyAttrFlags) {
ParseVectorDataArray(flags, KeyAttrFlags);
}
}
// ------------------------------------------------------------------------------------------------
AnimationCurve::~AnimationCurve() {
// empty
}
// ------------------------------------------------------------------------------------------------
AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name,
const Document &doc, const char *const *target_prop_whitelist /*= NULL*/,
size_t whitelist_size /*= 0*/) :
Object(id, element, name), target(), doc(doc) {
const ScopePtr sc = GetRequiredScope(element);
// find target node
const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
for (const Connection *con : conns) {
// link should go for a property
if (!con->PropertyName().length()) {
continue;
}
Object *object = con->DestinationObject();
if (!object) {
DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", element);
continue;
}
target = object;
prop = con->PropertyName();
break;
}
props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
}
// ------------------------------------------------------------------------------------------------
AnimationCurveNode::~AnimationCurveNode() {
curves.clear();
}
// ------------------------------------------------------------------------------------------------
const AnimationMap &AnimationCurveNode::Curves() const {
/* Lazy loaded animation curves, will only load if required */
if (curves.empty()) {
// resolve attached animation curves
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
for (const Connection *con : conns) {
// So the advantage of having this STL boilerplate is that it's dead simple once you get it.
// The other advantage is casting is guaranteed to be safe and nullptr will be returned in the last step if it fails.
Object *ob = con->SourceObject();
AnimationCurve *anim_curve = dynamic_cast<AnimationCurve *>(ob);
ERR_CONTINUE_MSG(!anim_curve, "Failed to convert animation curve from object");
curves.insert(std::make_pair(con->PropertyName(), anim_curve));
}
}
return curves;
}
// ------------------------------------------------------------------------------------------------
AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name), doc(doc) {
const ScopePtr sc = GetRequiredScope(element);
// note: the props table here bears little importance and is usually absent
props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
}
// ------------------------------------------------------------------------------------------------
AnimationLayer::~AnimationLayer() {
// empty
}
// ------------------------------------------------------------------------------------------------
const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist,
size_t whitelist_size /*= 0*/) const {
AnimationCurveNodeList nodes;
// resolve attached animation nodes
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode");
nodes.reserve(conns.size());
for (const Connection *con : conns) {
// link should not go to a property
if (con->PropertyName().length()) {
continue;
}
Object *ob = con->SourceObject();
if (!ob) {
DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", element);
continue;
}
const AnimationCurveNode *anim = dynamic_cast<AnimationCurveNode *>(ob);
if (!anim) {
DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode", element);
continue;
}
if (target_prop_whitelist) {
const char *s = anim->TargetProperty().c_str();
bool ok = false;
for (size_t i = 0; i < whitelist_size; ++i) {
if (!strcmp(s, target_prop_whitelist[i])) {
ok = true;
break;
}
}
if (!ok) {
continue;
}
}
nodes.push_back(anim);
}
return nodes;
}
// ------------------------------------------------------------------------------------------------
AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
// note: we don't currently use any of these properties so we shouldn't bother if it is missing
props = GetPropertyTable(doc, "AnimationStack.FbxAnimStack", element, sc, true);
// resolve attached animation layers
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
layers.reserve(conns.size());
for (const Connection *con : conns) {
// link should not go to a property
if (con->PropertyName().length()) {
continue;
}
Object *ob = con->SourceObject();
if (!ob) {
DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", element);
continue;
}
const AnimationLayer *anim = dynamic_cast<const AnimationLayer *>(ob);
if (!anim) {
DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", element);
continue;
}
layers.push_back(anim);
}
}
// ------------------------------------------------------------------------------------------------
AnimationStack::~AnimationStack() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
} // namespace FBXDocParser

View File

@ -0,0 +1,470 @@
/*************************************************************************/
/* FBXBinaryTokenizer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXBinaryTokenizer.cpp
* @brief Implementation of a fake lexer for binary fbx files -
* we emit tokens so the parser needs almost no special handling
* for binary files.
*/
#include "ByteSwapper.h"
#include "FBXTokenizer.h"
#include "core/print_string.h"
#include <stdint.h>
namespace FBXDocParser {
//enum Flag
//{
// e_unknown_0 = 1 << 0,
// e_unknown_1 = 1 << 1,
// e_unknown_2 = 1 << 2,
// e_unknown_3 = 1 << 3,
// e_unknown_4 = 1 << 4,
// e_unknown_5 = 1 << 5,
// e_unknown_6 = 1 << 6,
// e_unknown_7 = 1 << 7,
// e_unknown_8 = 1 << 8,
// e_unknown_9 = 1 << 9,
// e_unknown_10 = 1 << 10,
// e_unknown_11 = 1 << 11,
// e_unknown_12 = 1 << 12,
// e_unknown_13 = 1 << 13,
// e_unknown_14 = 1 << 14,
// e_unknown_15 = 1 << 15,
// e_unknown_16 = 1 << 16,
// e_unknown_17 = 1 << 17,
// e_unknown_18 = 1 << 18,
// e_unknown_19 = 1 << 19,
// e_unknown_20 = 1 << 20,
// e_unknown_21 = 1 << 21,
// e_unknown_22 = 1 << 22,
// e_unknown_23 = 1 << 23,
// e_flag_field_size_64_bit = 1 << 24, // Not sure what is
// e_unknown_25 = 1 << 25,
// e_unknown_26 = 1 << 26,
// e_unknown_27 = 1 << 27,
// e_unknown_28 = 1 << 28,
// e_unknown_29 = 1 << 29,
// e_unknown_30 = 1 << 30,
// e_unknown_31 = 1 << 31
//};
//
//bool check_flag(uint32_t flags, Flag to_check)
//{
// return (flags & to_check) != 0;
//}
// ------------------------------------------------------------------------------------------------
Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) :
sbegin(sbegin),
send(send),
type(type),
line(offset),
column(BINARY_MARKER) {
#ifdef DEBUG_ENABLED
contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
#endif
// calc length
// measure from sBegin to sEnd and validate?
}
namespace {
// ------------------------------------------------------------------------------------------------
// signal tokenization error
void TokenizeError(const std::string &message, size_t offset) {
print_error("[FBX-Tokenize] " + String(message.c_str()) + ", offset " + itos(offset));
}
// ------------------------------------------------------------------------------------------------
size_t Offset(const char *begin, const char *cursor) {
//ai_assert(begin <= cursor);
return cursor - begin;
}
// ------------------------------------------------------------------------------------------------
void TokenizeError(const std::string &message, const char *begin, const char *cursor) {
TokenizeError(message, Offset(begin, cursor));
}
// ------------------------------------------------------------------------------------------------
uint32_t ReadWord(const char *input, const char *&cursor, const char *end) {
const size_t k_to_read = sizeof(uint32_t);
if (Offset(cursor, end) < k_to_read) {
TokenizeError("cannot ReadWord, out of bounds", input, cursor);
}
uint32_t word;
::memcpy(&word, cursor, 4);
AI_SWAP4(word);
cursor += k_to_read;
return word;
}
// ------------------------------------------------------------------------------------------------
uint64_t ReadDoubleWord(const char *input, const char *&cursor, const char *end) {
const size_t k_to_read = sizeof(uint64_t);
if (Offset(cursor, end) < k_to_read) {
TokenizeError("cannot ReadDoubleWord, out of bounds", input, cursor);
}
uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/;
::memcpy(&dword, cursor, sizeof(uint64_t));
AI_SWAP8(dword);
cursor += k_to_read;
return dword;
}
// ------------------------------------------------------------------------------------------------
uint8_t ReadByte(const char *input, const char *&cursor, const char *end) {
if (Offset(cursor, end) < sizeof(uint8_t)) {
TokenizeError("cannot ReadByte, out of bounds", input, cursor);
}
uint8_t word; /* = *reinterpret_cast< const uint8_t* >( cursor )*/
::memcpy(&word, cursor, sizeof(uint8_t));
++cursor;
return word;
}
// ------------------------------------------------------------------------------------------------
unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const char *input,
const char *&cursor, const char *end, bool long_length = false, bool allow_null = false) {
const uint32_t len_len = long_length ? 4 : 1;
if (Offset(cursor, end) < len_len) {
TokenizeError("cannot ReadString, out of bounds reading length", input, cursor);
}
const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end);
if (Offset(cursor, end) < length) {
TokenizeError("cannot ReadString, length is out of bounds", input, cursor);
}
sbegin_out = cursor;
cursor += length;
send_out = cursor;
if (!allow_null) {
for (unsigned int i = 0; i < length; ++i) {
if (sbegin_out[i] == '\0') {
TokenizeError("failed ReadString, unexpected NUL character in string", input, cursor);
}
}
}
return length;
}
// ------------------------------------------------------------------------------------------------
void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end) {
if (Offset(cursor, end) < 1) {
TokenizeError("cannot ReadData, out of bounds reading length", input, cursor);
}
const char type = *cursor;
sbegin_out = cursor++;
switch (type) {
// 16 bit int
case 'Y':
cursor += 2;
break;
// 1 bit bool flag (yes/no)
case 'C':
cursor += 1;
break;
// 32 bit int
case 'I':
// <- fall through
// float
case 'F':
cursor += 4;
break;
// double
case 'D':
cursor += 8;
break;
// 64 bit int
case 'L':
cursor += 8;
break;
// note: do not write cursor += ReadWord(...cursor) as this would be UB
// raw binary data
case 'R': {
const uint32_t length = ReadWord(input, cursor, end);
cursor += length;
break;
}
case 'b':
// TODO: what is the 'b' type code? Right now we just skip over it /
// take the full range we could get
cursor = end;
break;
// array of *
case 'f':
case 'd':
case 'l':
case 'i':
case 'c': {
const uint32_t length = ReadWord(input, cursor, end);
const uint32_t encoding = ReadWord(input, cursor, end);
const uint32_t comp_len = ReadWord(input, cursor, end);
// compute length based on type and check against the stored value
if (encoding == 0) {
uint32_t stride = 0;
switch (type) {
case 'f':
case 'i':
stride = 4;
break;
case 'd':
case 'l':
stride = 8;
break;
case 'c':
stride = 1;
break;
default:
break;
};
//ai_assert(stride > 0);
if (length * stride != comp_len) {
TokenizeError("cannot ReadData, calculated data stride differs from what the file claims", input, cursor);
}
}
// zip/deflate algorithm (encoding==1)? take given length. anything else? die
else if (encoding != 1) {
TokenizeError("cannot ReadData, unknown encoding", input, cursor);
}
cursor += comp_len;
break;
}
// string
case 'S': {
const char *sb, *se;
// 0 characters can legally happen in such strings
ReadString(sb, se, input, cursor, end, true, true);
break;
}
default:
TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor);
}
if (cursor > end) {
TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor);
}
// the type code is contained in the returned range
send_out = cursor;
}
// ------------------------------------------------------------------------------------------------
bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits) {
// the first word contains the offset at which this block ends
const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
// we may get 0 if reading reached the end of the file -
// fbx files have a mysterious extra footer which I don't know
// how to extract any information from, but at least it always
// starts with a 0.
if (!end_offset) {
return false;
}
if (end_offset > Offset(input, end)) {
TokenizeError("block offset is out of range", input, cursor);
} else if (end_offset < Offset(input, cursor)) {
TokenizeError("block offset is negative out of range", input, cursor);
}
// the second data word contains the number of properties in the scope
const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
// the third data word contains the length of the property list
const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
// now comes the name of the scope/key
const char *sbeg, *send;
ReadString(sbeg, send, input, cursor, end);
output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor)));
// now come the individual properties
const char *begin_cursor = cursor;
for (unsigned int i = 0; i < prop_count; ++i) {
ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor)));
if (i != prop_count - 1) {
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor)));
}
}
if (Offset(begin_cursor, cursor) != prop_length) {
TokenizeError("property length not reached, something is wrong", input, cursor);
}
// at the end of each nested block, there is a NUL record to indicate
// that the sub-scope exists (i.e. to distinguish between P: and P : {})
// this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit.
const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t) * 3 + 1) : (sizeof(uint32_t) * 3 + 1);
if (Offset(input, cursor) < end_offset) {
if (end_offset - Offset(input, cursor) < sentinel_block_length) {
TokenizeError("insufficient padding bytes at block end", input, cursor);
}
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor)));
// XXX this is vulnerable to stack overflowing ..
while (Offset(input, cursor) < end_offset - sentinel_block_length) {
ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
}
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor)));
for (unsigned int i = 0; i < sentinel_block_length; ++i) {
if (cursor[i] != '\0') {
TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor);
}
}
cursor += sentinel_block_length;
}
if (Offset(input, cursor) != end_offset) {
TokenizeError("scope length not reached, something is wrong", input, cursor);
}
return true;
}
} // anonymous namespace
// ------------------------------------------------------------------------------------------------
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length) {
if (length < 0x1b) {
//TokenizeError("file is too short",0);
}
//uint32_t offset = 0x15;
/* const char* cursor = input + 0x15;
const uint32_t flags = ReadWord(input, cursor, input + length);
const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused
const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/
if (strncmp(input, "Kaydara FBX Binary", 18)) {
TokenizeError("magic bytes not found", 0);
}
const char *cursor = input + 18;
/*Result ignored*/ ReadByte(input, cursor, input + length);
/*Result ignored*/ ReadByte(input, cursor, input + length);
/*Result ignored*/ ReadByte(input, cursor, input + length);
/*Result ignored*/ ReadByte(input, cursor, input + length);
/*Result ignored*/ ReadByte(input, cursor, input + length);
const uint32_t version = ReadWord(input, cursor, input + length);
print_verbose("FBX Version: " + itos(version));
//ASSIMP_LOG_DEBUG_F("FBX version: ", version);
const bool is64bits = version >= 7500;
const char *end = input + length;
while (cursor < end) {
if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
break;
}
}
}
} // namespace FBXDocParser

View File

@ -0,0 +1,110 @@
/*************************************************************************/
/* FBXCommon.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXCommon.h
* Some useful constants and enums for dealing with FBX files.
*/
#ifndef FBX_COMMON_H
#define FBX_COMMON_H
#include <string>
namespace FBXDocParser {
const std::string NULL_RECORD = { // 13 null bytes
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
}; // who knows why
const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings
const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
const int64_t SECOND = 46186158000; // FBX's kTime unit
// rotation order. We'll probably use EulerXYZ for everything
enum RotOrder {
RotOrder_EulerXYZ = 0,
RotOrder_EulerXZY,
RotOrder_EulerYZX,
RotOrder_EulerYXZ,
RotOrder_EulerZXY,
RotOrder_EulerZYX,
RotOrder_SphericXYZ,
RotOrder_MAX // end-of-enum sentinel
};
enum TransformInheritance {
Transform_RrSs = 0,
Transform_RSrs = 1,
Transform_Rrs = 2,
TransformInheritance_MAX // end-of-enum sentinel
};
} // namespace FBXDocParser
#endif // FBX_COMMON_H

View File

@ -0,0 +1,279 @@
/*************************************************************************/
/* FBXDeformer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXNoteAttribute.cpp
* @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
*/
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXMeshGeometry.h"
#include "FBXParser.h"
#include "core/math/math_funcs.h"
#include "core/math/transform.h"
#include <iostream>
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
props = GetPropertyTable(doc, "Deformer.Fbx" + classname, element, sc, true);
}
// ------------------------------------------------------------------------------------------------
Deformer::~Deformer() {
}
Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
// used something.fbx as this is a cache name.
props = GetPropertyTable(doc, "Something.Fbx" + classname, element, sc, true);
}
Constraint::~Constraint() {
}
// ------------------------------------------------------------------------------------------------
Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Deformer(id, element, doc, name), valid_transformAssociateModel(false) {
const ScopePtr sc = GetRequiredScope(element);
// for( auto element : sc.Elements())
// {
// std::cout << "cluster element: " << element.first << std::endl;
// }
//
// element: Indexes
// element: Transform
// element: TransformAssociateModel
// element: TransformLink
// element: UserData
// element: Version
// element: Weights
const ElementPtr Indexes = sc->GetElement("Indexes");
const ElementPtr Weights = sc->GetElement("Weights");
const ElementPtr TransformAssociateModel = sc->GetElement("TransformAssociateModel");
if (TransformAssociateModel != nullptr) {
//Transform t = ReadMatrix(*TransformAssociateModel);
link_mode = SkinLinkMode_Additive;
valid_transformAssociateModel = true;
} else {
link_mode = SkinLinkMode_Normalized;
valid_transformAssociateModel = false;
}
const ElementPtr Transform = GetRequiredElement(sc, "Transform", element);
const ElementPtr TransformLink = GetRequiredElement(sc, "TransformLink", element);
// todo: check if we need this
//const Element& TransformAssociateModel = GetRequiredElement(sc, "TransformAssociateModel", &element);
transform = ReadMatrix(Transform);
transformLink = ReadMatrix(TransformLink);
// it is actually possible that there be Deformer's with no weights
if (!!Indexes != !!Weights) {
DOMError("either Indexes or Weights are missing from Cluster", element);
}
if (Indexes) {
ParseVectorDataArray(indices, Indexes);
ParseVectorDataArray(weights, Weights);
}
if (indices.size() != weights.size()) {
DOMError("sizes of index and weight array don't match up", element);
}
// read assigned node
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Model");
for (const Connection *con : conns) {
const Model *mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
if (mod) {
node = mod;
break;
}
}
if (!node) {
DOMError("failed to read target Node for Cluster", element);
node = nullptr;
}
}
// ------------------------------------------------------------------------------------------------
Cluster::~Cluster() {
}
// ------------------------------------------------------------------------------------------------
Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Deformer(id, element, doc, name), accuracy(0.0f) {
const ScopePtr sc = GetRequiredScope(element);
// keep this it is used for debugging and any FBX format changes
// for (auto element : sc.Elements()) {
// std::cout << "skin element: " << element.first << std::endl;
// }
const ElementPtr Link_DeformAcuracy = sc->GetElement("Link_DeformAcuracy");
if (Link_DeformAcuracy) {
accuracy = ParseTokenAsFloat(GetRequiredToken(Link_DeformAcuracy, 0));
}
const ElementPtr SkinType = sc->GetElement("SkinningType");
if (SkinType) {
std::string skin_type = ParseTokenAsString(GetRequiredToken(SkinType, 0));
if (skin_type == "Linear") {
skinType = Skin_Linear;
} else if (skin_type == "Rigid") {
skinType = Skin_Rigid;
} else if (skin_type == "DualQuaternion") {
skinType = Skin_DualQuaternion;
} else if (skin_type == "Blend") {
skinType = Skin_Blend;
} else {
print_error("[doc:skin] could not find valid skin type: " + String(skin_type.c_str()));
}
}
// resolve assigned clusters
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
//
clusters.reserve(conns.size());
for (const Connection *con : conns) {
const Cluster *cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
if (cluster) {
clusters.push_back(cluster);
continue;
}
}
}
// ------------------------------------------------------------------------------------------------
Skin::~Skin() {
}
// ------------------------------------------------------------------------------------------------
BlendShape::BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Deformer(id, element, doc, name) {
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
blendShapeChannels.reserve(conns.size());
for (const Connection *con : conns) {
const BlendShapeChannel *bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
if (bspc) {
blendShapeChannels.push_back(bspc);
continue;
}
}
}
// ------------------------------------------------------------------------------------------------
BlendShape::~BlendShape() {
}
// ------------------------------------------------------------------------------------------------
BlendShapeChannel::BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Deformer(id, element, doc, name) {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr DeformPercent = sc->GetElement("DeformPercent");
if (DeformPercent) {
percent = ParseTokenAsFloat(GetRequiredToken(DeformPercent, 0));
}
const ElementPtr FullWeights = sc->GetElement("FullWeights");
if (FullWeights) {
ParseVectorDataArray(fullWeights, FullWeights);
}
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry");
shapeGeometries.reserve(conns.size());
for (const Connection *con : conns) {
const ShapeGeometry *const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
if (sg) {
shapeGeometries.push_back(sg);
continue;
}
}
}
// ------------------------------------------------------------------------------------------------
BlendShapeChannel::~BlendShapeChannel() {
}
// ------------------------------------------------------------------------------------------------
} // namespace FBXDocParser

View File

@ -0,0 +1,699 @@
/*************************************************************************/
/* FBXDocument.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXDocument.cpp
* @brief Implementation of the FBX DOM classes
*/
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXImportSettings.h"
#include "FBXMeshGeometry.h"
#include "FBXParser.h"
#include "FBXProperties.h"
#include "FBXUtil.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
LazyObject::LazyObject(uint64_t id, const ElementPtr element, const Document &doc) :
doc(doc), element(element), id(id), flags() {
// empty
}
// ------------------------------------------------------------------------------------------------
LazyObject::~LazyObject() {
object.reset();
}
ObjectPtr LazyObject::LoadObject() {
if (IsBeingConstructed() || FailedToConstruct()) {
return nullptr;
}
if (object) {
return object.get();
}
TokenPtr key = element->KeyToken();
ERR_FAIL_COND_V(!key, nullptr);
const TokenList &tokens = element->Tokens();
if (tokens.size() < 3) {
//DOMError("expected at least 3 tokens: id, name and class tag",&element);
return nullptr;
}
const char *err = nullptr;
std::string name = ParseTokenAsString(tokens[1], err);
if (err) {
DOMError(err, element);
}
// small fix for binary reading: binary fbx files don't use
// prefixes such as Model:: in front of their names. The
// loading code expects this at many places, though!
// so convert the binary representation (a 0x0001) to the
// double colon notation.
if (tokens[1]->IsBinary()) {
for (size_t i = 0; i < name.length(); ++i) {
if (name[i] == 0x0 && name[i + 1] == 0x1) {
name = name.substr(i + 2) + "::" + name.substr(0, i);
}
}
}
const std::string classtag = ParseTokenAsString(tokens[2], err);
if (err) {
DOMError(err, element);
}
// prevent recursive calls
flags |= BEING_CONSTRUCTED;
// this needs to be relatively fast since it happens a lot,
// so avoid constructing strings all the time.
const char *obtype = key->begin();
const size_t length = static_cast<size_t>(key->end() - key->begin());
if (!strncmp(obtype, "Pose", length)) {
object.reset(new FbxPose(id, element, doc, name));
} else if (!strncmp(obtype, "Geometry", length)) {
if (!strcmp(classtag.c_str(), "Mesh")) {
object.reset(new MeshGeometry(id, element, name, doc));
}
if (!strcmp(classtag.c_str(), "Shape")) {
object.reset(new ShapeGeometry(id, element, name, doc));
}
if (!strcmp(classtag.c_str(), "Line")) {
object.reset(new LineGeometry(id, element, name, doc));
}
} else if (!strncmp(obtype, "NodeAttribute", length)) {
if (!strcmp(classtag.c_str(), "Camera")) {
object.reset(new Camera(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "CameraSwitcher")) {
object.reset(new CameraSwitcher(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "Light")) {
object.reset(new Light(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "Null")) {
object.reset(new Null(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "LimbNode")) {
// This is an older format for bones
// this is what blender uses I believe
object.reset(new LimbNode(id, element, doc, name));
}
} else if (!strncmp(obtype, "Constraint", length)) {
object.reset(new Constraint(id, element, doc, name));
} else if (!strncmp(obtype, "Deformer", length)) {
if (!strcmp(classtag.c_str(), "Cluster")) {
object.reset(new Cluster(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "Skin")) {
object.reset(new Skin(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "BlendShape")) {
object.reset(new BlendShape(id, element, doc, name));
} else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) {
object.reset(new BlendShapeChannel(id, element, doc, name));
}
} else if (!strncmp(obtype, "Model", length)) {
// Model is normal node
// LimbNode model is a 'bone' node.
if (!strcmp(classtag.c_str(), "LimbNode")) {
object.reset(new ModelLimbNode(id, element, doc, name));
} else if (strcmp(classtag.c_str(), "IKEffector") && strcmp(classtag.c_str(), "FKEffector")) {
// FK and IK effectors are not supporte
object.reset(new Model(id, element, doc, name));
}
} else if (!strncmp(obtype, "Material", length)) {
object.reset(new Material(id, element, doc, name));
} else if (!strncmp(obtype, "Texture", length)) {
object.reset(new Texture(id, element, doc, name));
} else if (!strncmp(obtype, "LayeredTexture", length)) {
object.reset(new LayeredTexture(id, element, doc, name));
} else if (!strncmp(obtype, "Video", length)) {
object.reset(new Video(id, element, doc, name));
} else if (!strncmp(obtype, "AnimationStack", length)) {
object.reset(new AnimationStack(id, element, name, doc));
} else if (!strncmp(obtype, "AnimationLayer", length)) {
object.reset(new AnimationLayer(id, element, name, doc));
} else if (!strncmp(obtype, "AnimationCurve", length)) {
object.reset(new AnimationCurve(id, element, name, doc));
} else if (!strncmp(obtype, "AnimationCurveNode", length)) {
object.reset(new AnimationCurveNode(id, element, name, doc));
} else {
ERR_FAIL_V_MSG(nullptr, "FBX contains unsupported object: " + String(obtype));
}
flags &= ~BEING_CONSTRUCTED;
return object.get();
}
// ------------------------------------------------------------------------------------------------
Object::Object(uint64_t id, const ElementPtr element, const std::string &name) :
element(element), name(name), id(id) {
}
// ------------------------------------------------------------------------------------------------
Object::~Object() {
// empty
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::FileGlobalSettings(const Document &doc, const PropertyTable *props) :
props(props), doc(doc) {
// empty
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::~FileGlobalSettings() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
// ------------------------------------------------------------------------------------------------
Document::Document(const Parser &parser, const ImportSettings &settings) :
settings(settings), parser(parser), SafeToImport(false) {
// Cannot use array default initialization syntax because vc8 fails on it
for (unsigned int &timeStamp : creationTimeStamp) {
timeStamp = 0;
}
// we must check if we can read the header version safely, if its outdated then drop it.
if (ReadHeader()) {
SafeToImport = true;
ReadPropertyTemplates();
ReadGlobalSettings();
// This order is important, connections need parsed objects to check
// whether connections are ok or not. Objects may not be evaluated yet,
// though, since this may require valid connections.
ReadObjects();
ReadConnections();
}
}
// ------------------------------------------------------------------------------------------------
Document::~Document() {
for (PropertyTemplateMap::value_type v : templates) {
delete v.second;
}
for (ObjectMap::value_type &v : objects) {
delete v.second;
}
for (ConnectionMap::value_type &v : src_connections) {
delete v.second;
}
// clear globals import pointer
globals.reset();
}
// ------------------------------------------------------------------------------------------------
static const unsigned int LowerSupportedVersion = 7100;
static const unsigned int UpperSupportedVersion = 7700;
bool Document::ReadHeader() {
// Read ID objects from "Objects" section
const ScopePtr sc = parser.GetRootScope();
const ElementPtr ehead = sc->GetElement("FBXHeaderExtension");
if (!ehead || !ehead->Compound()) {
DOMError("no FBXHeaderExtension dictionary found");
}
const ScopePtr shead = ehead->Compound();
fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0));
// While we may have some success with newer files, we don't support
// the older 6.n fbx format
if (fbxVersion < LowerSupportedVersion) {
DOMWarning("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013, you can re-export using Maya, 3DS, blender or Autodesk FBX tool");
return false;
}
if (fbxVersion > UpperSupportedVersion) {
DOMWarning("unsupported, newer format version, supported are only FBX 2011, up to FBX 2020"
" trying to read it nevertheless");
}
const ElementPtr ecreator = shead->GetElement("Creator");
if (ecreator) {
creator = ParseTokenAsString(GetRequiredToken(ecreator, 0));
}
const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp");
if (etimestamp && etimestamp->Compound()) {
const ScopePtr stimestamp = etimestamp->Compound();
creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Year"), 0));
creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Month"), 0));
creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Day"), 0));
creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Hour"), 0));
creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Minute"), 0));
creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Second"), 0));
creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Millisecond"), 0));
}
return true;
}
// ------------------------------------------------------------------------------------------------
void Document::ReadGlobalSettings() {
ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported");
const ScopePtr sc = parser.GetRootScope();
const ElementPtr ehead = sc->GetElement("GlobalSettings");
if (nullptr == ehead || !ehead->Compound()) {
DOMWarning("no GlobalSettings dictionary found");
globals = std::make_shared<FileGlobalSettings>(*this, new PropertyTable());
return;
}
const PropertyTable *props = GetPropertyTable(*this, "", ehead, ehead->Compound(), true);
//double v = PropertyGet<float>( *props, std::string("UnitScaleFactor"), 1.0 );
if (!props) {
DOMError("GlobalSettings dictionary contains no property table");
}
globals = std::make_shared<FileGlobalSettings>(*this, props);
}
// ------------------------------------------------------------------------------------------------
void Document::ReadObjects() {
// read ID objects from "Objects" section
const ScopePtr sc = parser.GetRootScope();
const ElementPtr eobjects = sc->GetElement("Objects");
if (!eobjects || !eobjects->Compound()) {
DOMError("no Objects dictionary found");
}
// add a dummy entry to represent the Model::RootNode object (id 0),
// which is only indirectly defined in the input file
objects[0] = new LazyObject(0L, eobjects, *this);
const ScopePtr sobjects = eobjects->Compound();
for (const ElementMap::value_type &iter : sobjects->Elements()) {
// extract ID
const TokenList &tok = iter.second->Tokens();
if (tok.empty()) {
DOMError("expected ID after object key", iter.second);
}
const char *err;
const uint64_t id = ParseTokenAsID(tok[0], err);
if (err) {
DOMError(err, iter.second);
}
// id=0 is normally implicit
if (id == 0L) {
DOMError("encountered object with implicitly defined id 0", iter.second);
}
if (objects.find(id) != objects.end()) {
DOMWarning("encountered duplicate object id, ignoring first occurrence", iter.second);
}
objects[id] = new LazyObject(id, iter.second, *this);
// grab all animation stacks upfront since there is no listing of them
if (!strcmp(iter.first.c_str(), "AnimationStack")) {
animationStacks.push_back(id);
} else if (!strcmp(iter.first.c_str(), "Constraint")) {
constraints.push_back(id);
} else if (!strcmp(iter.first.c_str(), "Pose")) {
bind_poses.push_back(id);
} else if (!strcmp(iter.first.c_str(), "Material")) {
materials.push_back(id);
} else if (!strcmp(iter.first.c_str(), "Deformer")) {
TokenPtr key = iter.second->KeyToken();
ERR_CONTINUE_MSG(!key, "[parser bug] invalid token key for deformer");
const TokenList &tokens = iter.second->Tokens();
const std::string class_tag = ParseTokenAsString(tokens[2], err);
if (err) {
DOMError(err, iter.second);
}
if (class_tag == "Skin") {
//print_verbose("registered skin:" + itos(id));
skins.push_back(id);
}
}
}
}
// ------------------------------------------------------------------------------------------------
void Document::ReadPropertyTemplates() {
const ScopePtr sc = parser.GetRootScope();
// read property templates from "Definitions" section
const ElementPtr edefs = sc->GetElement("Definitions");
if (!edefs || !edefs->Compound()) {
DOMWarning("no Definitions dictionary found");
return;
}
const ScopePtr sdefs = edefs->Compound();
const ElementCollection otypes = sdefs->GetCollection("ObjectType");
for (ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
const ElementPtr el = (*it).second;
const ScopePtr sc_2 = el->Compound();
if (!sc_2) {
DOMWarning("expected nested scope in ObjectType, ignoring", el);
continue;
}
const TokenList &tok = el->Tokens();
if (tok.empty()) {
DOMWarning("expected name for ObjectType element, ignoring", el);
continue;
}
const std::string &oname = ParseTokenAsString(tok[0]);
const ElementCollection templs = sc_2->GetCollection("PropertyTemplate");
for (ElementMap::const_iterator iter = templs.first; iter != templs.second; ++iter) {
const ElementPtr el_2 = (*iter).second;
const ScopePtr sc_3 = el_2->Compound();
if (!sc_3) {
DOMWarning("expected nested scope in PropertyTemplate, ignoring", el);
continue;
}
const TokenList &tok_2 = el_2->Tokens();
if (tok_2.empty()) {
DOMWarning("expected name for PropertyTemplate element, ignoring", el);
continue;
}
const std::string &pname = ParseTokenAsString(tok_2[0]);
const ElementPtr Properties70 = sc_3->GetElement("Properties70");
if (Properties70) {
// PropertyTable(const ElementPtr element, const PropertyTable* templateProps);
const PropertyTable *props = new PropertyTable(Properties70, nullptr);
templates[oname + "." + pname] = props;
}
}
}
}
// ------------------------------------------------------------------------------------------------
void Document::ReadConnections() {
const ScopePtr sc = parser.GetRootScope();
// read property templates from "Definitions" section
const ElementPtr econns = sc->GetElement("Connections");
if (!econns || !econns->Compound()) {
DOMError("no Connections dictionary found");
}
uint64_t insertionOrder = 0l;
const ScopePtr sconns = econns->Compound();
const ElementCollection conns = sconns->GetCollection("C");
for (ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
const ElementPtr el = (*it).second;
const std::string &type = ParseTokenAsString(GetRequiredToken(el, 0));
// PP = property-property connection, ignored for now
// (tokens: "PP", ID1, "Property1", ID2, "Property2")
if (type == "PP") {
continue;
}
const uint64_t src = ParseTokenAsID(GetRequiredToken(el, 1));
const uint64_t dest = ParseTokenAsID(GetRequiredToken(el, 2));
// OO = object-object connection
// OP = object-property connection, in which case the destination property follows the object ID
const std::string &prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el, 3)) : "");
if (objects.find(src) == objects.end()) {
DOMWarning("source object for connection does not exist", el);
continue;
}
// dest may be 0 (root node) but we added a dummy object before
if (objects.find(dest) == objects.end()) {
DOMWarning("destination object for connection does not exist", el);
continue;
}
// add new connection
const Connection *const c = new Connection(insertionOrder++, src, dest, prop, *this);
src_connections.insert(ConnectionMap::value_type(src, c));
dest_connections.insert(ConnectionMap::value_type(dest, c));
}
}
// ------------------------------------------------------------------------------------------------
const std::vector<const AnimationStack *> &Document::AnimationStacks() const {
if (!animationStacksResolved.empty() || animationStacks.empty()) {
return animationStacksResolved;
}
animationStacksResolved.reserve(animationStacks.size());
for (uint64_t id : animationStacks) {
LazyObject *lazy = GetObject(id);
// Two things happen here:
// We cast internally an Object PTR to an Animation Stack PTR
// We return invalid weak_ptrs for objects which are invalid
const AnimationStack *stack = lazy->Get<AnimationStack>();
ERR_CONTINUE_MSG(!stack, "invalid ptr to AnimationStack - conversion failure");
// We push back the weak reference :) to keep things simple, as ownership is on the parser side so it wont be cleaned up.
animationStacksResolved.push_back(stack);
}
return animationStacksResolved;
}
// ------------------------------------------------------------------------------------------------
LazyObject *Document::GetObject(uint64_t id) const {
ObjectMap::const_iterator it = objects.find(id);
return it == objects.end() ? nullptr : (*it).second;
}
#define MAX_CLASSNAMES 6
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap &conns) const {
std::vector<const Connection *> temp;
const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range =
conns.equal_range(id);
temp.reserve(std::distance(range.first, range.second));
for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
temp.push_back((*it).second);
}
std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
return temp; // NRVO should handle this
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
const ConnectionMap &conns,
const char *const *classnames,
size_t count) const
{
size_t lengths[MAX_CLASSNAMES];
const size_t c = count;
for (size_t i = 0; i < c; ++i) {
lengths[i] = strlen(classnames[i]);
}
std::vector<const Connection *> temp;
const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range =
conns.equal_range(id);
temp.reserve(std::distance(range.first, range.second));
for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
TokenPtr key = (is_src ? (*it).second->LazyDestinationObject() : (*it).second->LazySourceObject())->GetElement()->KeyToken();
const char *obtype = key->begin();
for (size_t i = 0; i < c; ++i) {
//ai_assert(classnames[i]);
if (static_cast<size_t>(std::distance(key->begin(), key->end())) == lengths[i] && !strncmp(classnames[i], obtype, lengths[i])) {
obtype = nullptr;
break;
}
}
if (obtype) {
continue;
}
temp.push_back((*it).second);
}
std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
return temp; // NRVO should handle this
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source) const {
return GetConnectionsSequenced(source, ConnectionsBySource());
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t src, const char *classname) const {
const char *arr[] = { classname };
return GetConnectionsBySourceSequenced(src, arr, 1);
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source,
const char *const *classnames, size_t count) const {
return GetConnectionsSequenced(source, true, ConnectionsBySource(), classnames, count);
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
const char *classname) const {
const char *arr[] = { classname };
return GetConnectionsByDestinationSequenced(dest, arr, 1);
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const {
return GetConnectionsSequenced(dest, ConnectionsByDestination());
}
// ------------------------------------------------------------------------------------------------
std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
const char *const *classnames, size_t count) const {
return GetConnectionsSequenced(dest, false, ConnectionsByDestination(), classnames, count);
}
// ------------------------------------------------------------------------------------------------
Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop,
const Document &doc) :
insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) {
}
// ------------------------------------------------------------------------------------------------
Connection::~Connection() {
// empty
}
// ------------------------------------------------------------------------------------------------
LazyObject *Connection::LazySourceObject() const {
LazyObject *const lazy = doc.GetObject(src);
return lazy;
}
// ------------------------------------------------------------------------------------------------
LazyObject *Connection::LazyDestinationObject() const {
LazyObject *const lazy = doc.GetObject(dest);
return lazy;
}
// ------------------------------------------------------------------------------------------------
Object *Connection::SourceObject() const {
LazyObject *lazy = doc.GetObject(src);
//ai_assert(lazy);
return lazy->LoadObject();
}
// ------------------------------------------------------------------------------------------------
Object *Connection::DestinationObject() const {
LazyObject *lazy = doc.GetObject(dest);
//ai_assert(lazy);
return lazy->LoadObject();
}
} // namespace FBXDocParser

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
/*************************************************************************/
/* FBXDocumentUtil.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXDocumentUtil.cpp
* @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h
*/
#include "FBXDocumentUtil.h"
#include "FBXDocument.h"
#include "FBXParser.h"
#include "FBXProperties.h"
#include "FBXUtil.h"
#include "core/print_string.h"
namespace FBXDocParser {
namespace Util {
void DOMError(const std::string &message) {
print_error("[FBX-DOM]" + String(message.c_str()));
}
void DOMError(const std::string &message, const Token *token) {
print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
}
void DOMError(const std::string &message, const std::shared_ptr<Token> token) {
print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
}
void DOMError(const std::string &message, const Element *element /*= NULL*/) {
if (element) {
DOMError(message, element->KeyToken());
}
print_error("[FBX-DOM] " + String(message.c_str()));
}
void DOMError(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) {
if (element) {
DOMError(message, element->KeyToken());
}
print_error("[FBX-DOM] " + String(message.c_str()));
}
void DOMWarning(const std::string &message) {
print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
}
void DOMWarning(const std::string &message, const Token *token) {
print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
}
void DOMWarning(const std::string &message, const Element *element /*= NULL*/) {
if (element) {
DOMWarning(message, element->KeyToken());
return;
}
print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
}
void DOMWarning(const std::string &message, const std::shared_ptr<Token> token) {
print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
}
void DOMWarning(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) {
if (element) {
DOMWarning(message, element->KeyToken());
return;
}
print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
}
// ------------------------------------------------------------------------------------------------
// fetch a property table and the corresponding property template
const PropertyTable *GetPropertyTable(const Document &doc,
const std::string &templateName,
const ElementPtr element,
const ScopePtr sc,
bool no_warn /*= false*/) {
// todo: make this an abstraction
const ElementPtr Properties70 = sc->GetElement("Properties70");
const PropertyTable *templateProps = static_cast<const PropertyTable *>(nullptr);
if (templateName.length()) {
PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
if (it != doc.Templates().end()) {
templateProps = (*it).second;
}
}
if (!Properties70 || !Properties70->Compound()) {
if (!no_warn) {
DOMWarning("property table (Properties70) not found", element);
}
if (templateProps) {
return templateProps;
} else {
return new const PropertyTable();
}
}
return new PropertyTable(Properties70, templateProps);
}
} // namespace Util
} // namespace FBXDocParser

View File

@ -0,0 +1,142 @@
/*************************************************************************/
/* FBXDocumentUtil.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2012, 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 FBXDocumentUtil.h
* @brief FBX internal utilities used by the DOM reading code
*/
#ifndef FBX_DOCUMENT_UTIL_H
#define FBX_DOCUMENT_UTIL_H
#include "FBXDocument.h"
#include <memory>
#include <string>
struct Token;
struct Element;
namespace FBXDocParser {
namespace Util {
// Parser errors
void DOMError(const std::string &message);
void DOMError(const std::string &message, const Token *token);
void DOMError(const std::string &message, const Element *element);
void DOMError(const std::string &message, const std::shared_ptr<Element> element);
void DOMError(const std::string &message, const std::shared_ptr<Token> token);
// Parser warnings
void DOMWarning(const std::string &message);
void DOMWarning(const std::string &message, const Token *token);
void DOMWarning(const std::string &message, const Element *element);
void DOMWarning(const std::string &message, const std::shared_ptr<Token> token);
void DOMWarning(const std::string &message, const std::shared_ptr<Element> element);
// fetch a property table and the corresponding property template
const PropertyTable *GetPropertyTable(const Document &doc,
const std::string &templateName,
const ElementPtr element,
const ScopePtr sc,
bool no_warn = false);
// ------------------------------------------------------------------------------------------------
template <typename T>
const T *ProcessSimpleConnection(const Connection &con,
bool is_object_property_conn,
const char *name,
const ElementPtr element,
const char **propNameOut = nullptr) {
if (is_object_property_conn && !con.PropertyName().length()) {
DOMWarning("expected incoming " + std::string(name) +
" link to be an object-object connection, ignoring",
element);
return nullptr;
} else if (!is_object_property_conn && con.PropertyName().length()) {
DOMWarning("expected incoming " + std::string(name) +
" link to be an object-property connection, ignoring",
element);
return nullptr;
}
if (is_object_property_conn && propNameOut) {
// note: this is ok, the return value of PropertyValue() is guaranteed to
// remain valid and unchanged as long as the document exists.
*propNameOut = con.PropertyName().c_str();
}
// Cast Object to AnimationPlayer for example using safe functions, which return nullptr etc
Object *ob = con.SourceObject();
ERR_FAIL_COND_V_MSG(!ob, nullptr, "Failed to load object from SourceObject ptr");
return dynamic_cast<const T *>(ob);
}
} // namespace Util
} // namespace FBXDocParser
#endif // FBX_DOCUMENT_UTIL_H

View File

@ -0,0 +1,174 @@
/*************************************************************************/
/* FBXImportSettings.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXImportSettings.h
* @brief FBX importer runtime configuration
*/
#ifndef FBX_IMPORT_SETTINGS_H
#define FBX_IMPORT_SETTINGS_H
namespace FBXDocParser {
/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */
struct ImportSettings {
ImportSettings() :
strictMode(true), readAllLayers(true), readAllMaterials(true), readMaterials(true), readTextures(true), readCameras(true), readLights(true), readAnimations(true), readWeights(true), preservePivots(true), optimizeEmptyAnimationCurves(true), useLegacyEmbeddedTextureNaming(false), removeEmptyBones(true), convertToMeters(false) {
// empty
}
/** enable strict mode:
* - only accept fbx 2012, 2013 files
* - on the slightest error, give up.
*
* Basically, strict mode means that the fbx file will actually
* be validated. Strict mode is off by default. */
bool strictMode;
/** specifies whether all geometry layers are read and scanned for
* usable data channels. The FBX spec indicates that many readers
* will only read the first channel and that this is in some way
* the recommended way- in reality, however, it happens a lot that
* vertex data is spread among multiple layers. The default
* value for this option is true.*/
bool readAllLayers;
/** specifies whether all materials are read, or only those that
* are referenced by at least one mesh. Reading all materials
* may make FBX reading a lot slower since all objects
* need to be processed .
* This bit is ignored unless readMaterials=true*/
bool readAllMaterials;
/** import materials (true) or skip them and assign a default
* material. The default value is true.*/
bool readMaterials;
/** import embedded textures? Default value is true.*/
bool readTextures;
/** import cameras? Default value is true.*/
bool readCameras;
/** import light sources? Default value is true.*/
bool readLights;
/** import animations (i.e. animation curves, the node
* skeleton is always imported). Default value is true. */
bool readAnimations;
/** read bones (vertex weights and deform info).
* Default value is true. */
bool readWeights;
/** preserve transformation pivots and offsets. Since these can
* not directly be represented in assimp, additional dummy
* nodes will be generated. Note that settings this to false
* can make animation import a lot slower. The default value
* is true.
*
* The naming scheme for the generated nodes is:
* <OriginalName>_$AssimpFbx$_<TransformName>
*
* where <TransformName> is one of
* RotationPivot
* RotationOffset
* PreRotation
* PostRotation
* ScalingPivot
* ScalingOffset
* Translation
* Scaling
* Rotation
**/
bool preservePivots;
/** do not import animation curves that specify a constant
* values matching the corresponding node transformation.
* The default value is true. */
bool optimizeEmptyAnimationCurves;
/** use legacy naming for embedded textures eg: (*0, *1, *2)
*/
bool useLegacyEmbeddedTextureNaming;
/** Empty bones shall be removed
*/
bool removeEmptyBones;
/** Set to true to perform a conversion from cm to meter after the import
*/
bool convertToMeters;
};
} // namespace FBXDocParser
#endif // FBX_IMPORT_SETTINGS_H

View File

@ -0,0 +1,406 @@
/*************************************************************************/
/* FBXMaterial.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2020, 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 FBXMaterial.cpp
* @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation
*/
#include "ByteSwapper.h"
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXImportSettings.h"
#include "FBXParser.h"
#include "FBXProperties.h"
#include "FBXUtil.h"
#include <algorithm> // std::transform
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Material::Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr ShadingModel = sc->GetElement("ShadingModel");
const ElementPtr MultiLayer = sc->GetElement("MultiLayer");
if (MultiLayer) {
multilayer = !!ParseTokenAsInt(GetRequiredToken(MultiLayer, 0));
}
if (ShadingModel) {
shading = ParseTokenAsString(GetRequiredToken(ShadingModel, 0));
} else {
DOMWarning("shading mode not specified, assuming phong", element);
shading = "phong";
}
std::string templateName;
if (shading == "phong") {
templateName = "Material.FbxSurfacePhong";
} else if (shading == "lambert") {
templateName = "Material.FbxSurfaceLambert";
} else {
DOMWarning("shading mode not recognized: " + shading, element);
}
props = GetPropertyTable(doc, templateName, element, sc);
// resolve texture links
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
for (const Connection *con : conns) {
// texture link to properties, not objects
if (!con->PropertyName().length()) {
continue;
}
Object *ob = con->SourceObject();
if (!ob) {
DOMWarning("failed to read source object for texture link, ignoring", element);
continue;
}
const Texture *tex = dynamic_cast<const Texture *>(ob);
if (!tex) {
LayeredTexture *layeredTexture = dynamic_cast<LayeredTexture *>(ob);
if (!layeredTexture) {
DOMWarning("source object for texture link is not a texture or layered texture, ignoring", element);
continue;
}
const std::string &prop = con->PropertyName();
if (layeredTextures.find(prop) != layeredTextures.end()) {
DOMWarning("duplicate layered texture link: " + prop, element);
}
layeredTextures[prop] = layeredTexture;
layeredTexture->fillTexture(doc);
} else {
const std::string &prop = con->PropertyName();
if (textures.find(prop) != textures.end()) {
DOMWarning("duplicate texture link: " + prop, element);
}
textures[prop] = tex;
}
}
}
// ------------------------------------------------------------------------------------------------
Material::~Material() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
// ------------------------------------------------------------------------------------------------
Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name), uvScaling(1.0f, 1.0f), media(nullptr) {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr Type = sc->GetElement("Type");
const ElementPtr FileName = sc->GetElement("FileName");
const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
const ElementPtr ModelUVTranslation = sc->GetElement("ModelUVTranslation");
const ElementPtr ModelUVScaling = sc->GetElement("ModelUVScaling");
const ElementPtr Texture_Alpha_Source = sc->GetElement("Texture_Alpha_Source");
const ElementPtr Cropping = sc->GetElement("Cropping");
if (Type) {
type = ParseTokenAsString(GetRequiredToken(Type, 0));
}
if (FileName) {
fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
}
if (RelativeFilename) {
relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
}
if (ModelUVTranslation) {
uvTrans = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 0)),
ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 1)));
}
if (ModelUVScaling) {
uvScaling = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 0)),
ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 1)));
}
if (Cropping) {
crop[0] = ParseTokenAsInt(GetRequiredToken(Cropping, 0));
crop[1] = ParseTokenAsInt(GetRequiredToken(Cropping, 1));
crop[2] = ParseTokenAsInt(GetRequiredToken(Cropping, 2));
crop[3] = ParseTokenAsInt(GetRequiredToken(Cropping, 3));
} else {
// vc8 doesn't support the crop() syntax in initialization lists
// (and vc9 WARNS about the new (i.e. compliant) behaviour).
crop[0] = crop[1] = crop[2] = crop[3] = 0;
}
if (Texture_Alpha_Source) {
alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0));
}
props = GetPropertyTable(doc, "Texture.FbxFileTexture", element, sc);
// 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
bool ok;
const Vector3 &scaling = PropertyGet<Vector3>(props, "Scaling", ok);
if (ok) {
uvScaling.x = scaling.x;
uvScaling.y = scaling.y;
}
const Vector3 &trans = PropertyGet<Vector3>(props, "Translation", ok);
if (ok) {
uvTrans.x = trans.x;
uvTrans.y = trans.y;
}
// resolve video links
if (doc.Settings().readTextures) {
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
for (const Connection *con : conns) {
const Object *const ob = con->SourceObject();
if (!ob) {
DOMWarning("failed to read source object for texture link, ignoring", element);
continue;
}
const Video *const video = dynamic_cast<const Video *>(ob);
if (video) {
media = video;
}
}
}
}
Texture::~Texture() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) :
Object(id, element, name), blendMode(BlendMode_Modulate), alpha(1) {
const ScopePtr sc = GetRequiredScope(element);
ElementPtr BlendModes = sc->GetElement("BlendModes");
ElementPtr Alphas = sc->GetElement("Alphas");
if (BlendModes != 0) {
blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(BlendModes, 0));
}
if (Alphas != 0) {
alpha = ParseTokenAsFloat(GetRequiredToken(Alphas, 0));
}
}
LayeredTexture::~LayeredTexture() {
}
void LayeredTexture::fillTexture(const Document &doc) {
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
for (size_t i = 0; i < conns.size(); ++i) {
const Connection *con = conns.at(i);
const Object *const ob = con->SourceObject();
if (!ob) {
DOMWarning("failed to read source object for texture link, ignoring", element);
continue;
}
const Texture *const tex = dynamic_cast<const Texture *>(ob);
textures.push_back(tex);
}
}
// ------------------------------------------------------------------------------------------------
Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name), contentLength(0), content(0) {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr Type = sc->GetElement("Type");
// File Version 7500 Crashes if this is not checked fully.
// As of writing this comment 7700 exists, in August 2020
ElementPtr FileName = nullptr;
if (HasElement(sc, "Filename")) {
FileName = (ElementPtr)sc->GetElement("Filename");
} else if (HasElement(sc, "FileName")) {
FileName = (ElementPtr)sc->GetElement("FileName");
} else {
print_error("file has invalid video material returning...");
return;
}
const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
const ElementPtr Content = sc->GetElement("Content");
if (Type) {
type = ParseTokenAsString(GetRequiredToken(Type, 0));
}
if (FileName) {
fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
}
if (RelativeFilename) {
relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
}
if (Content && !Content->Tokens().empty()) {
//this field is omitted when the embedded texture is already loaded, let's ignore if it's not found
try {
const Token *token = GetRequiredToken(Content, 0);
const char *data = token->begin();
if (!token->IsBinary()) {
if (*data != '"') {
DOMError("embedded content is not surrounded by quotation marks", element);
} else {
size_t targetLength = 0;
auto numTokens = Content->Tokens().size();
// First time compute size (it could be large like 64Gb and it is good to allocate it once)
for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
const Token *dataToken = GetRequiredToken(Content, tokenIdx);
size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
const char *base64data = dataToken->begin() + 1;
const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength);
if (outLength == 0) {
DOMError("Corrupted embedded content found", element);
}
targetLength += outLength;
}
if (targetLength == 0) {
DOMError("Corrupted embedded content found", element);
}
content = new uint8_t[targetLength];
contentLength = static_cast<uint64_t>(targetLength);
size_t dst_offset = 0;
for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
const Token *dataToken = GetRequiredToken(Content, tokenIdx);
ERR_FAIL_COND(!dataToken);
size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
const char *base64data = dataToken->begin() + 1;
dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset);
}
if (targetLength != dst_offset) {
delete[] content;
contentLength = 0;
DOMError("Corrupted embedded content found", element);
}
}
} else if (static_cast<size_t>(token->end() - data) < 5) {
DOMError("binary data array is too short, need five (5) bytes for type signature and element count", element);
} else if (*data != 'R') {
DOMWarning("video content is not raw binary data, ignoring", element);
} else {
// read number of elements
uint32_t len = 0;
::memcpy(&len, data + 1, sizeof(len));
AI_SWAP4(len);
contentLength = len;
content = new uint8_t[len];
::memcpy(content, data + 5, len);
}
} catch (...) {
// //we don't need the content data for contents that has already been loaded
// ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
// runtimeError.what());
}
}
props = GetPropertyTable(doc, "Video.FbxVideo", element, sc);
}
Video::~Video() {
if (content) {
delete[] content;
}
if (props != nullptr) {
delete props;
props = nullptr;
}
}
} // namespace FBXDocParser

View File

@ -0,0 +1,451 @@
/*************************************************************************/
/* FBXMeshGeometry.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXMeshGeometry.cpp
* @brief Assimp::FBX::MeshGeometry implementation
*/
#include <functional>
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXImportSettings.h"
#include "FBXMeshGeometry.h"
#include "core/math/vector3.h"
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Geometry::Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Object(id, element, name), skin() {
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
for (const Connection *con : conns) {
const Skin *sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
if (sk) {
skin = sk;
}
const BlendShape *bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry",
element);
if (bsp) {
blendShapes.push_back(bsp);
}
}
}
// ------------------------------------------------------------------------------------------------
Geometry::~Geometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<const BlendShape *> &Geometry::get_blend_shapes() const {
return blendShapes;
}
// ------------------------------------------------------------------------------------------------
const Skin *Geometry::DeformerSkin() const {
return skin;
}
// ------------------------------------------------------------------------------------------------
MeshGeometry::MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Geometry(id, element, name, doc) {
print_verbose("mesh name: " + String(name.c_str()));
ScopePtr sc = element->Compound();
ERR_FAIL_COND_MSG(sc == nullptr, "failed to read geometry, prevented crash");
ERR_FAIL_COND_MSG(!HasElement(sc, "Vertices"), "Detected mesh with no vertexes, didn't populate the mesh");
// must have Mesh elements:
const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
const ElementPtr PolygonVertexIndex = GetRequiredElement(sc, "PolygonVertexIndex", element);
if (HasElement(sc, "Edges")) {
const ElementPtr element_edges = GetRequiredElement(sc, "Edges", element);
ParseVectorDataArray(m_edges, element_edges);
}
// read mesh data into arrays
ParseVectorDataArray(m_vertices, Vertices);
ParseVectorDataArray(m_face_indices, PolygonVertexIndex);
ERR_FAIL_COND_MSG(m_vertices.empty(), "mesh with no vertexes in FBX file, did you mean to delete it?");
ERR_FAIL_COND_MSG(m_face_indices.empty(), "mesh has no faces, was this intended?");
// Retrieve layer elements, for all of the mesh
const ElementCollection &Layer = sc->GetCollection("Layer");
// Store all layers
std::vector<std::tuple<int, std::string> > valid_layers;
// now read the sub mesh information from the geometry (normals, uvs, etc)
for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
const ScopePtr layer = GetRequiredScope(it->second);
const ElementCollection &LayerElement = layer->GetCollection("LayerElement");
for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
std::string layer_name = eit->first;
ElementPtr element_layer = eit->second;
const ScopePtr layer_element = GetRequiredScope(element_layer);
// Actual usable 'type' LayerElementUV, LayerElementNormal, etc
const ElementPtr Type = GetRequiredElement(layer_element, "Type");
const ElementPtr TypedIndex = GetRequiredElement(layer_element, "TypedIndex");
const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0));
const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0));
// we only need the layer name and the typed index.
valid_layers.push_back(std::tuple<int, std::string>(typedIndex, type));
}
}
// get object / mesh directly from the FBX by the element ID.
const ScopePtr top = GetRequiredScope(element);
// iterate over all layers for the mesh (uvs, normals, smoothing groups, colors, etc)
for (size_t x = 0; x < valid_layers.size(); x++) {
const int layer_id = std::get<0>(valid_layers[x]);
const std::string &layer_type_name = std::get<1>(valid_layers[x]);
// Get collection of elements from the XLayerMap (example: LayerElementUV)
// this must contain our proper elements.
// This is stupid, because it means we select them ALL not just the one we want.
// but it's fine we can match by id.
GetRequiredElement(top, layer_type_name);
const ElementCollection &candidates = top->GetCollection(layer_type_name);
ElementMap::const_iterator iter;
for (iter = candidates.first; iter != candidates.second; ++iter) {
const ScopePtr layer_scope = GetRequiredScope(iter->second);
TokenPtr layer_token = GetRequiredToken(iter->second, 0);
const int index = ParseTokenAsInt(layer_token);
ERR_FAIL_COND_MSG(layer_scope == nullptr, "prevented crash, layer scope is invalid");
if (index == layer_id) {
const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(layer_scope, "MappingInformationType"), 0));
const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(layer_scope, "ReferenceInformationType"), 0));
if (layer_type_name == "LayerElementUV") {
if (index == 0) {
m_uv_0 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
} else if (index == 1) {
m_uv_1 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
}
} else if (layer_type_name == "LayerElementMaterial") {
m_material_allocation_ids = resolve_vertex_data_array<int>(layer_scope, MappingInformationType, ReferenceInformationType, "Materials");
} else if (layer_type_name == "LayerElementNormal") {
m_normals = resolve_vertex_data_array<Vector3>(layer_scope, MappingInformationType, ReferenceInformationType, "Normals");
} else if (layer_type_name == "LayerElementColor") {
m_colors = resolve_vertex_data_array<Color>(layer_scope, MappingInformationType, ReferenceInformationType, "Colors");
}
}
}
}
print_verbose("Mesh statistics \nuv_0: " + m_uv_0.debug_info() + "\nuv_1: " + m_uv_1.debug_info() + "\nvertices: " + itos(m_vertices.size()));
// Compose the edge of the mesh.
// You can see how the edges are stored into the FBX here: https://gist.github.com/AndreaCatania/da81840f5aa3b2feedf189e26c5a87e6
for (size_t i = 0; i < m_edges.size(); i += 1) {
ERR_FAIL_INDEX_MSG((size_t)m_edges[i], m_face_indices.size(), "The edge is pointing to a weird location in the face indices. The FBX is corrupted.");
int polygon_vertex_0 = m_face_indices[m_edges[i]];
int polygon_vertex_1;
if (polygon_vertex_0 < 0) {
// The polygon_vertex_0 points to the end of a polygon, so it's
// connected with the beginning of polygon in the edge list.
// Fist invert the vertex.
polygon_vertex_0 = ~polygon_vertex_0;
// Search the start vertex of the polygon.
// Iterate from the polygon_vertex_index backward till the start of
// the polygon is found.
ERR_FAIL_COND_MSG(m_edges[i] - 1 < 0, "The polygon is not yet started and we already need the final vertex. This FBX is corrupted.");
bool found_it = false;
for (int x = m_edges[i] - 1; x >= 0; x -= 1) {
if (x == 0) {
// This for sure is the start.
polygon_vertex_1 = m_face_indices[x];
found_it = true;
break;
} else if (m_face_indices[x] < 0) {
// This is the end of the previous polygon, so the next is
// the start of the polygon we need.
polygon_vertex_1 = m_face_indices[x + 1];
found_it = true;
break;
}
}
// As the algorithm above, this check is useless. Because the first
// ever vertex is always considered the begining of a polygon.
ERR_FAIL_COND_MSG(found_it == false, "Was not possible to find the first vertex of this polygon. FBX file is corrupted.");
} else {
ERR_FAIL_INDEX_MSG((size_t)(m_edges[i] + 1), m_face_indices.size(), "FBX The other FBX edge seems to point to an invalid vertices. This FBX file is corrupted.");
// Take the next vertex
polygon_vertex_1 = m_face_indices[m_edges[i] + 1];
}
if (polygon_vertex_1 < 0) {
// We don't care if the `polygon_vertex_1` is the end of the polygon,
// for `polygon_vertex_1` so we can just invert it.
polygon_vertex_1 = ~polygon_vertex_1;
}
ERR_FAIL_COND_MSG(polygon_vertex_0 == polygon_vertex_1, "The vertices of this edge can't be the same, Is this a point???. This FBX file is corrupted.");
// Just create the edge.
edge_map.push_back({ polygon_vertex_0, polygon_vertex_1 });
}
}
MeshGeometry::~MeshGeometry() {
// empty
}
const std::vector<Vector3> &MeshGeometry::get_vertices() const {
return m_vertices;
}
const std::vector<MeshGeometry::Edge> &MeshGeometry::get_edge_map() const {
return edge_map;
}
const std::vector<int> &MeshGeometry::get_polygon_indices() const {
return m_face_indices;
}
const std::vector<int> &MeshGeometry::get_edges() const {
return m_edges;
}
const MeshGeometry::MappingData<Vector3> &MeshGeometry::get_normals() const {
return m_normals;
}
const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_0() const {
//print_verbose("get uv_0 " + m_uv_0.debug_info() );
return m_uv_0;
}
const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_1() const {
//print_verbose("get uv_1 " + m_uv_1.debug_info() );
return m_uv_1;
}
const MeshGeometry::MappingData<Color> &MeshGeometry::get_colors() const {
return m_colors;
}
const MeshGeometry::MappingData<int> &MeshGeometry::get_material_allocation_id() const {
return m_material_allocation_ids;
}
int MeshGeometry::get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b) {
for (size_t i = 0; i < p_map.size(); i += 1) {
if ((p_map[i].vertex_0 == p_vertex_a && p_map[i].vertex_1 == p_vertex_b) || (p_map[i].vertex_1 == p_vertex_a && p_map[i].vertex_0 == p_vertex_b)) {
return i;
}
}
return -1;
}
MeshGeometry::Edge MeshGeometry::get_edge(const std::vector<Edge> &p_map, int p_id) {
ERR_FAIL_INDEX_V_MSG((size_t)p_id, p_map.size(), Edge({ -1, -1 }), "ID not found.");
return p_map[p_id];
}
template <class T>
MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array(
const ScopePtr source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType,
const std::string &dataElementName) {
ERR_FAIL_COND_V_MSG(source == nullptr, MappingData<T>(), "Invalid scope operator preventing memory corruption");
// UVIndex, MaterialIndex, NormalIndex, etc..
std::string indexDataElementName = dataElementName + "Index";
// goal: expand everything to be per vertex
ReferenceType l_ref_type = ReferenceType::direct;
// Read the reference type into the enumeration
if (ReferenceInformationType == "IndexToDirect") {
l_ref_type = ReferenceType::index_to_direct;
} else if (ReferenceInformationType == "Index") {
// set non legacy index to direct mapping
l_ref_type = ReferenceType::index;
} else if (ReferenceInformationType == "Direct") {
l_ref_type = ReferenceType::direct;
} else {
ERR_FAIL_V_MSG(MappingData<T>(), "invalid reference type has the FBX format changed?");
}
MapType l_map_type = MapType::none;
if (MappingInformationType == "None") {
l_map_type = MapType::none;
} else if (MappingInformationType == "ByVertice") {
l_map_type = MapType::vertex;
} else if (MappingInformationType == "ByPolygonVertex") {
l_map_type = MapType::polygon_vertex;
} else if (MappingInformationType == "ByPolygon") {
l_map_type = MapType::polygon;
} else if (MappingInformationType == "ByEdge") {
l_map_type = MapType::edge;
} else if (MappingInformationType == "AllSame") {
l_map_type = MapType::all_the_same;
} else {
print_error("invalid mapping type: " + String(MappingInformationType.c_str()));
}
// create mapping data
MeshGeometry::MappingData<T> tempData;
tempData.map_type = l_map_type;
tempData.ref_type = l_ref_type;
// parse data into array
ParseVectorDataArray(tempData.data, GetRequiredElement(source, dataElementName));
// index array wont always exist
const ElementPtr element = GetOptionalElement(source, indexDataElementName);
if (element) {
ParseVectorDataArray(tempData.index, element);
}
return tempData;
}
// ------------------------------------------------------------------------------------------------
ShapeGeometry::ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Geometry(id, element, name, doc) {
const ScopePtr sc = element->Compound();
if (nullptr == sc) {
DOMError("failed to read Geometry object (class: Shape), no data scope found");
}
const ElementPtr Indexes = GetRequiredElement(sc, "Indexes", element);
const ElementPtr Normals = GetRequiredElement(sc, "Normals", element);
const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
ParseVectorDataArray(m_indices, Indexes);
ParseVectorDataArray(m_vertices, Vertices);
ParseVectorDataArray(m_normals, Normals);
}
// ------------------------------------------------------------------------------------------------
ShapeGeometry::~ShapeGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<Vector3> &ShapeGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<Vector3> &ShapeGeometry::GetNormals() const {
return m_normals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<unsigned int> &ShapeGeometry::GetIndices() const {
return m_indices;
}
// ------------------------------------------------------------------------------------------------
LineGeometry::LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
Geometry(id, element, name, doc) {
const ScopePtr sc = element->Compound();
if (!sc) {
DOMError("failed to read Geometry object (class: Line), no data scope found");
}
const ElementPtr Points = GetRequiredElement(sc, "Points", element);
const ElementPtr PointsIndex = GetRequiredElement(sc, "PointsIndex", element);
ParseVectorDataArray(m_vertices, Points);
ParseVectorDataArray(m_indices, PointsIndex);
}
// ------------------------------------------------------------------------------------------------
LineGeometry::~LineGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<Vector3> &LineGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<int> &LineGeometry::GetIndices() const {
return m_indices;
}
} // namespace FBXDocParser

View File

@ -0,0 +1,263 @@
/*************************************************************************/
/* FBXMeshGeometry.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
----------------------------------------------------------------------
*/
#ifndef FBX_MESH_GEOMETRY_H
#define FBX_MESH_GEOMETRY_H
#include "core/color.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
#include "core/vector.h"
#include "FBXDocument.h"
#include "FBXParser.h"
#include <iostream>
#define AI_MAX_NUMBER_OF_TEXTURECOORDS 4
#define AI_MAX_NUMBER_OF_COLOR_SETS 8
namespace FBXDocParser {
/*
* DOM base class for all kinds of FBX geometry
*/
class Geometry : public Object {
public:
Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
virtual ~Geometry();
/** Get the Skin attached to this geometry or NULL */
const Skin *DeformerSkin() const;
const std::vector<const BlendShape *> &get_blend_shapes() const;
size_t get_blend_shape_count() const {
return blendShapes.size();
}
private:
const Skin *skin;
std::vector<const BlendShape *> blendShapes;
};
typedef std::vector<int> MatIndexArray;
/// Map Geometry stores the FBX file information.
///
/// # FBX doc.
/// ## Reference type declared:
/// - Direct (directly related to the mapping information type)
/// - IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
///
/// ## Map Type:
/// * None The mapping is undetermined.
/// * ByVertex There will be one mapping coordinate for each surface control point/vertex (ControlPoint is a vertex).
/// * If you have direct reference type verticies[x]
/// * If you have IndexToDirect reference type the UV
/// * ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
/// * ByPolygon There can be only one mapping coordinate for the whole polygon.
/// * One mapping per polygon polygon x has this normal x
/// * For each vertex of the polygon then set the normal to x
/// * ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
/// * AllSame There can be only one mapping coordinate for the whole surface.
class MeshGeometry : public Geometry {
public:
enum class MapType {
none = 0, // No mapping type. Stored as "None".
vertex, // Maps per vertex. Stored as "ByVertice".
polygon_vertex, // Maps per polygon vertex. Stored as "ByPolygonVertex".
polygon, // Maps per polygon. Stored as "ByPolygon".
edge, // Maps per edge. Stored as "ByEdge".
all_the_same // Uaps to everything. Stored as "AllSame".
};
enum class ReferenceType {
direct = 0,
index = 1,
index_to_direct = 2
};
template <class T>
struct MappingData {
MapType map_type = MapType::none;
ReferenceType ref_type = ReferenceType::direct;
std::vector<T> data;
/// The meaning of the indices depends from the `MapType`.
/// If `ref_type` is `direct` this map is hollow.
std::vector<int> index;
String debug_info() const {
return "indexes: " + itos(index.size()) + " data: " + itos(data.size());
}
};
struct Edge {
int vertex_0 = 0, vertex_1 = 0;
Edge(int v0, int v1) :
vertex_0(v0), vertex_1(v1) {}
Edge() {}
};
public:
MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
virtual ~MeshGeometry();
const std::vector<Vector3> &get_vertices() const;
const std::vector<Edge> &get_edge_map() const;
const std::vector<int> &get_polygon_indices() const;
const std::vector<int> &get_edges() const;
const MappingData<Vector3> &get_normals() const;
const MappingData<Vector2> &get_uv_0() const;
const MappingData<Vector2> &get_uv_1() const;
const MappingData<Color> &get_colors() const;
const MappingData<int> &get_material_allocation_id() const;
/// Returns -1 if the vertices doesn't form an edge. Vertex order, doesn't
// matter.
static int get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b);
// Retuns the edge point bu that ID, or the edge with -1 vertices if the
// id is not valid.
static Edge get_edge(const std::vector<Edge> &p_map, int p_id);
private:
// Read directly from the FBX file.
std::vector<Vector3> m_vertices;
std::vector<Edge> edge_map;
std::vector<int> m_face_indices;
std::vector<int> m_edges;
MappingData<Vector3> m_normals;
MappingData<Vector2> m_uv_0; // first uv coordinates
MappingData<Vector2> m_uv_1; // second uv coordinates
MappingData<Color> m_colors; // colors for the mesh
MappingData<int> m_material_allocation_ids; // slot of material used
template <class T>
MappingData<T> resolve_vertex_data_array(
const ScopePtr source,
const std::string &MappingInformationType,
const std::string &ReferenceInformationType,
const std::string &dataElementName);
};
/*
* DOM class for FBX geometry of type "Shape"
*/
class ShapeGeometry : public Geometry {
public:
/** The class constructor */
ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
/** The class destructor */
virtual ~ShapeGeometry();
/** Get a list of all vertex points, non-unique*/
const std::vector<Vector3> &GetVertices() const;
/** Get a list of all vertex normals or an empty array if
* no normals are specified. */
const std::vector<Vector3> &GetNormals() const;
/** Return list of vertex indices. */
const std::vector<unsigned int> &GetIndices() const;
private:
std::vector<Vector3> m_vertices;
std::vector<Vector3> m_normals;
std::vector<unsigned int> m_indices;
};
/**
* DOM class for FBX geometry of type "Line"
*/
class LineGeometry : public Geometry {
public:
/** The class constructor */
LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
/** The class destructor */
virtual ~LineGeometry();
/** Get a list of all vertex points, non-unique*/
const std::vector<Vector3> &GetVertices() const;
/** Return list of vertex indices. */
const std::vector<int> &GetIndices() const;
private:
std::vector<Vector3> m_vertices;
std::vector<int> m_indices;
};
} // namespace FBXDocParser
#endif // FBX_MESH_GEOMETRY_H

View File

@ -0,0 +1,181 @@
/*************************************************************************/
/* FBXModel.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXModel.cpp
* @brief Assimp::FBX::Model implementation
*/
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXMeshGeometry.h"
#include "FBXParser.h"
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name), shading("Y") {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr Shading = sc->GetElement("Shading");
const ElementPtr Culling = sc->GetElement("Culling");
if (Shading) {
shading = GetRequiredToken(Shading, 0)->StringContents();
}
if (Culling) {
culling = ParseTokenAsString(GetRequiredToken(Culling, 0));
}
props = GetPropertyTable(doc, "Model.FbxNode", element, sc);
ResolveLinks(element, doc);
}
// ------------------------------------------------------------------------------------------------
Model::~Model() {
if (props != nullptr) {
delete props;
props = nullptr;
}
}
ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Model(id, element, doc, name){
};
ModelLimbNode::~ModelLimbNode() {
}
// ------------------------------------------------------------------------------------------------
void Model::ResolveLinks(const ElementPtr element, const Document &doc) {
const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };
// resolve material
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), arr, 3);
materials.reserve(conns.size());
geometry.reserve(conns.size());
attributes.reserve(conns.size());
for (const Connection *con : conns) {
// material and geometry links should be Object-Object connections
if (con->PropertyName().length()) {
continue;
}
const Object *const ob = con->SourceObject();
if (!ob) {
//DOMWarning("failed to read source object for incoming Model link, ignoring",&element);
continue;
}
const Material *const mat = dynamic_cast<const Material *>(ob);
if (mat) {
materials.push_back(mat);
continue;
}
const Geometry *const geo = dynamic_cast<const Geometry *>(ob);
if (geo) {
geometry.push_back(geo);
continue;
}
const NodeAttribute *const att = dynamic_cast<const NodeAttribute *>(ob);
if (att) {
attributes.push_back(att);
continue;
}
DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring", element);
continue;
}
}
// ------------------------------------------------------------------------------------------------
bool Model::IsNull() const {
const std::vector<const NodeAttribute *> &attrs = GetAttributes();
for (const NodeAttribute *att : attrs) {
const Null *null_tag = dynamic_cast<const Null *>(att);
if (null_tag) {
return true;
}
}
return false;
}
} // namespace FBXDocParser

View File

@ -0,0 +1,184 @@
/*************************************************************************/
/* FBXNodeAttribute.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXNoteAttribute.cpp
* @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
*/
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXParser.h"
#include <iostream>
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name), props() {
const ScopePtr sc = GetRequiredScope(element);
const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
// hack on the deriving type but Null/LimbNode attributes are the only case in which
// the property table is by design absent and no warning should be generated
// for it.
const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb);
}
// ------------------------------------------------------------------------------------------------
NodeAttribute::~NodeAttribute() {
// empty
}
// ------------------------------------------------------------------------------------------------
CameraSwitcher::CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
NodeAttribute(id, element, doc, name) {
const ScopePtr sc = GetRequiredScope(element);
const ElementPtr CameraId = sc->GetElement("CameraId");
const ElementPtr CameraName = sc->GetElement("CameraName");
const ElementPtr CameraIndexName = sc->GetElement("CameraIndexName");
if (CameraId) {
cameraId = ParseTokenAsInt(GetRequiredToken(CameraId, 0));
}
if (CameraName) {
cameraName = GetRequiredToken(CameraName, 0)->StringContents();
}
if (CameraIndexName && CameraIndexName->Tokens().size()) {
cameraIndexName = GetRequiredToken(CameraIndexName, 0)->StringContents();
}
}
// ------------------------------------------------------------------------------------------------
CameraSwitcher::~CameraSwitcher() {
// empty
}
// ------------------------------------------------------------------------------------------------
Camera::Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
NodeAttribute(id, element, doc, name) {
// empty
}
// ------------------------------------------------------------------------------------------------
Camera::~Camera() {
// empty
}
// ------------------------------------------------------------------------------------------------
Light::Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
NodeAttribute(id, element, doc, name) {
// empty
}
// ------------------------------------------------------------------------------------------------
Light::~Light() {
}
// ------------------------------------------------------------------------------------------------
Null::Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
NodeAttribute(id, element, doc, name) {
}
// ------------------------------------------------------------------------------------------------
Null::~Null() {
}
// ------------------------------------------------------------------------------------------------
LimbNode::LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
NodeAttribute(id, element, doc, name) {
//std::cout << "limb node: " << name << std::endl;
//const Scope &sc = GetRequiredScope(element);
//const ElementPtr const TypeFlag = sc["TypeFlags"];
// keep this it can dump new properties for you
// for( auto element : sc.Elements())
// {
// std::cout << "limbnode element: " << element.first << std::endl;
// }
// if(TypeFlag)
// {
// // std::cout << "type flag: " << GetRequiredToken(*TypeFlag, 0).StringContents() << std::endl;
// }
}
// ------------------------------------------------------------------------------------------------
LimbNode::~LimbNode() {
}
} // namespace FBXDocParser

View File

@ -0,0 +1,111 @@
/*************************************************************************/
/* FBXParseTools.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef FBX_PARSE_TOOLS_H
#define FBX_PARSE_TOOLS_H
#include "core/error_macros.h"
#include "core/ustring.h"
#include <stdint.h>
#include <algorithm>
#include <locale>
template <class char_t>
inline bool IsNewLine(char_t c) {
return c == '\n' || c == '\r';
}
template <class char_t>
inline bool IsSpace(char_t c) {
return (c == (char_t)' ' || c == (char_t)'\t');
}
template <class char_t>
inline bool IsSpaceOrNewLine(char_t c) {
return IsNewLine(c) || IsSpace(c);
}
template <class char_t>
inline bool IsLineEnd(char_t c) {
return (c == (char_t)'\r' || c == (char_t)'\n' || c == (char_t)'\0' || c == (char_t)'\f');
}
// ------------------------------------------------------------------------------------
// Special version of the function, providing higher accuracy and safety
// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
// ------------------------------------------------------------------------------------
inline uint64_t strtoul10_64(const char *in, bool &errored, const char **out = 0, unsigned int *max_inout = 0) {
unsigned int cur = 0;
uint64_t value = 0;
errored = *in < '0' || *in > '9';
ERR_FAIL_COND_V_MSG(errored, 0, "The string cannot be converted parser error");
for (;;) {
if (*in < '0' || *in > '9') {
break;
}
const uint64_t new_value = (value * (uint64_t)10) + ((uint64_t)(*in - '0'));
// numeric overflow, we rely on you
if (new_value < value) {
//WARN_PRINT( "Converting the string \" " + in + " \" into a value resulted in overflow." );
return 0;
}
value = new_value;
++in;
++cur;
if (max_inout && *max_inout == cur) {
if (out) { /* skip to end */
while (*in >= '0' && *in <= '9') {
++in;
}
*out = in;
}
return value;
}
}
if (out) {
*out = in;
}
if (max_inout) {
*max_inout = cur;
}
return value;
}
#endif // FBX_PARSE_TOOLS_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,264 @@
/*************************************************************************/
/* FBXParser.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXParser.h
* @brief FBX parsing code
*/
#ifndef FBX_PARSER_H
#define FBX_PARSER_H
#include <stdint.h>
#include <map>
#include <memory>
#include "core/color.h"
#include "core/math/transform.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
#include "FBXTokenizer.h"
namespace FBXDocParser {
class Scope;
class Parser;
class Element;
typedef Element *ElementPtr;
typedef Scope *ScopePtr;
typedef std::vector<ScopePtr> ScopeList;
typedef std::multimap<std::string, ElementPtr> ElementMap;
typedef std::pair<ElementMap::const_iterator, ElementMap::const_iterator> ElementCollection;
#define new_Scope new Scope
#define new_Element new Element
/** FBX data entity that consists of a key:value tuple.
*
* Example:
* @verbatim
* AnimationCurve: 23, "AnimCurve::", "" {
* [..]
* }
* @endverbatim
*
* As can be seen in this sample, elements can contain nested #Scope
* as their trailing member. **/
class Element {
public:
Element(TokenPtr key_token, Parser &parser);
~Element();
ScopePtr Compound() const {
return compound;
}
TokenPtr KeyToken() const {
return key_token;
}
const TokenList &Tokens() const {
return tokens;
}
private:
TokenList tokens;
ScopePtr compound = nullptr;
std::vector<ScopePtr> compound_scope;
TokenPtr key_token = nullptr;
};
/** FBX data entity that consists of a 'scope', a collection
* of not necessarily unique #Element instances.
*
* Example:
* @verbatim
* GlobalSettings: {
* Version: 1000
* Properties70:
* [...]
* }
* @endverbatim */
class Scope {
public:
Scope(Parser &parser, bool topLevel = false);
~Scope();
ElementPtr GetElement(const std::string &index) const {
ElementMap::const_iterator it = elements.find(index);
return it == elements.end() ? nullptr : (*it).second;
}
ElementPtr FindElementCaseInsensitive(const std::string &elementName) const {
for (auto element = elements.begin(); element != elements.end(); ++element) {
if (element->first.compare(elementName)) {
return element->second;
}
}
// nothing to reference / expired.
return nullptr;
}
ElementCollection GetCollection(const std::string &index) const {
return elements.equal_range(index);
}
const ElementMap &Elements() const {
return elements;
}
private:
ElementMap elements;
};
/** FBX parsing class, takes a list of input tokens and generates a hierarchy
* of nested #Scope instances, representing the fbx DOM.*/
class Parser {
public:
/** Parse given a token list. Does not take ownership of the tokens -
* the objects must persist during the entire parser lifetime */
Parser(const TokenList &tokens, bool is_binary);
~Parser();
const ScopePtr GetRootScope() const {
return root;
}
bool IsBinary() const {
return is_binary;
}
private:
friend class Scope;
friend class Element;
TokenPtr AdvanceToNextToken();
TokenPtr LastToken() const;
TokenPtr CurrentToken() const;
private:
ScopeList scopes;
const TokenList &tokens;
TokenPtr last = nullptr, current = nullptr;
TokenList::const_iterator cursor;
ScopePtr root = nullptr;
const bool is_binary;
};
/* token parsing - this happens when building the DOM out of the parse-tree*/
uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out);
size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out);
float ParseTokenAsFloat(const TokenPtr t, const char *&err_out);
int ParseTokenAsInt(const TokenPtr t, const char *&err_out);
int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out);
std::string ParseTokenAsString(const TokenPtr t, const char *&err_out);
/* wrapper around ParseTokenAsXXX() with DOMError handling */
uint64_t ParseTokenAsID(const TokenPtr t);
size_t ParseTokenAsDim(const TokenPtr t);
float ParseTokenAsFloat(const TokenPtr t);
int ParseTokenAsInt(const TokenPtr t);
int64_t ParseTokenAsInt64(const TokenPtr t);
std::string ParseTokenAsString(const TokenPtr t);
/* read data arrays */
void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el);
void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr ep);
void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el);
bool HasElement(const ScopePtr sc, const std::string &index);
// extract a required element from a scope, abort if the element cannot be found
const ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
const ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application)
const ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
// extract required compound scope
const ScopePtr GetRequiredScope(const ElementPtr el);
// get token at a particular index
TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index);
// ------------------------------------------------------------------------------------------------
// read a 4x4 matrix from an array of 16 floats
Transform ReadMatrix(const ElementPtr element);
} // namespace FBXDocParser
#endif // FBX_PARSER_H

View File

@ -0,0 +1,105 @@
/*************************************************************************/
/* FBXPose.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXNoteAttribute.cpp
* @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
*/
#include "FBXDocument.h"
#include "FBXParser.h"
#include <iostream>
namespace FBXDocParser {
class FbxPoseNode;
// ------------------------------------------------------------------------------------------------
FbxPose::FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
Object(id, element, name) {
const ScopePtr sc = GetRequiredScope(element);
//const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
const ElementCollection &PoseNodes = sc->GetCollection("PoseNode");
for (ElementMap::const_iterator it = PoseNodes.first; it != PoseNodes.second; ++it) {
std::string entry_name = (*it).first;
ElementPtr some_element = (*it).second;
FbxPoseNode *pose_node = new FbxPoseNode(some_element, doc, entry_name);
pose_nodes.push_back(pose_node);
}
}
// ------------------------------------------------------------------------------------------------
FbxPose::~FbxPose() {
pose_nodes.clear();
// empty
}
} // namespace FBXDocParser

View File

@ -0,0 +1,238 @@
/*************************************************************************/
/* FBXProperties.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXProperties.cpp
* @brief Implementation of the FBX dynamic properties system
*/
#include "FBXProperties.h"
#include "FBXDocumentUtil.h"
#include "FBXParser.h"
#include "FBXTokenizer.h"
namespace FBXDocParser {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Property::Property() {
}
// ------------------------------------------------------------------------------------------------
Property::~Property() {
}
namespace {
// ------------------------------------------------------------------------------------------------
// read a typed property out of a FBX element. The return value is NULL if the property cannot be read.
PropertyPtr ReadTypedProperty(const ElementPtr element) {
//ai_assert(element.KeyToken().StringContents() == "P");
const TokenList &tok = element->Tokens();
//ai_assert(tok.size() >= 5);
const std::string &s = ParseTokenAsString(tok[1]);
const char *const cs = s.c_str();
if (!strcmp(cs, "KString")) {
return new TypedProperty<std::string>(ParseTokenAsString(tok[4]));
} else if (!strcmp(cs, "bool") || !strcmp(cs, "Bool")) {
return new TypedProperty<bool>(ParseTokenAsInt(tok[4]) != 0);
} else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) {
return new TypedProperty<int>(ParseTokenAsInt(tok[4]));
} else if (!strcmp(cs, "ULongLong")) {
return new TypedProperty<uint64_t>(ParseTokenAsID(tok[4]));
} else if (!strcmp(cs, "KTime")) {
return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4]));
} else if (!strcmp(cs, "Vector3D") ||
!strcmp(cs, "ColorRGB") ||
!strcmp(cs, "Vector") ||
!strcmp(cs, "Color") ||
!strcmp(cs, "Lcl Translation") ||
!strcmp(cs, "Lcl Rotation") ||
!strcmp(cs, "Lcl Scaling")) {
return new TypedProperty<Vector3>(Vector3(
ParseTokenAsFloat(tok[4]),
ParseTokenAsFloat(tok[5]),
ParseTokenAsFloat(tok[6])));
} else if (!strcmp(cs, "double") || !strcmp(cs, "Number") || !strcmp(cs, "Float") || !strcmp(cs, "FieldOfView") || !strcmp(cs, "UnitScaleFactor")) {
return new TypedProperty<float>(ParseTokenAsFloat(tok[4]));
}
return nullptr;
}
// ------------------------------------------------------------------------------------------------
// peek into an element and check if it contains a FBX property, if so return its name.
std::string PeekPropertyName(const Element &element) {
//ai_assert(element.KeyToken().StringContents() == "P");
const TokenList &tok = element.Tokens();
if (tok.size() < 4) {
return "";
}
return ParseTokenAsString(tok[0]);
}
} // namespace
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable() :
templateProps(), element() {
}
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) :
templateProps(templateProps), element(element) {
const ScopePtr scope = GetRequiredScope(element);
ERR_FAIL_COND(!scope);
for (const ElementMap::value_type &v : scope->Elements()) {
if (v.first != "P") {
DOMWarning("expected only P elements in property table", v.second);
continue;
}
const std::string &name = PeekPropertyName(*v.second);
if (!name.length()) {
DOMWarning("could not read property name", v.second);
continue;
}
LazyPropertyMap::const_iterator it = lazyProps.find(name);
if (it != lazyProps.end()) {
DOMWarning("duplicate property name, will hide previous value: " + name, v.second);
continue;
}
// since the above checks for duplicates we can be sure to insert the only match here.
lazyProps[name] = v.second;
}
}
// ------------------------------------------------------------------------------------------------
PropertyTable::~PropertyTable() {
for (PropertyMap::value_type &v : props) {
delete v.second;
}
}
// ------------------------------------------------------------------------------------------------
PropertyPtr PropertyTable::Get(const std::string &name) const {
PropertyMap::const_iterator it = props.find(name);
if (it == props.end()) {
// hasn't been parsed yet?
LazyPropertyMap::const_iterator lit = lazyProps.find(name);
if (lit != lazyProps.end()) {
props[name] = ReadTypedProperty(lit->second);
it = props.find(name);
//ai_assert(it != props.end());
}
if (it == props.end()) {
// check property template
if (templateProps) {
return templateProps->Get(name);
}
return nullptr;
}
}
return (*it).second;
}
DirectPropertyMap PropertyTable::GetUnparsedProperties() const {
DirectPropertyMap result;
// Loop through all the lazy properties (which is all the properties)
for (const LazyPropertyMap::value_type &element : lazyProps) {
// Skip parsed properties
if (props.end() != props.find(element.first)) continue;
// Read the element's value.
// Wrap the naked pointer (since the call site is required to acquire ownership)
// std::unique_ptr from C++11 would be preferred both as a wrapper and a return value.
Property *prop = ReadTypedProperty(element.second);
// Element could not be read. Skip it.
if (!prop) continue;
// Add to result
result[element.first] = prop;
}
return result;
}
} // namespace FBXDocParser

View File

@ -0,0 +1,222 @@
/*************************************************************************/
/* FBXProperties.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXProperties.h
* @brief FBX dynamic properties
*/
#ifndef FBX_PROPERTIES_H
#define FBX_PROPERTIES_H
#include "FBXParser.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace FBXDocParser {
// Forward declarations
class Element;
/** Represents a dynamic property. Type info added by deriving classes,
* see #TypedProperty.
Example:
@verbatim
P: "ShininessExponent", "double", "Number", "",0.5
@endvebatim
*/
class Property {
protected:
Property();
public:
virtual ~Property();
public:
template <typename T>
const T *As() const {
return dynamic_cast<const T *>(this);
}
};
template <typename T>
class TypedProperty : public Property {
public:
explicit TypedProperty(const T &value) :
value(value) {
// empty
}
const T &Value() const {
return value;
}
private:
T value;
};
#define new_Property new Property
typedef Property *PropertyPtr;
typedef std::map<std::string, PropertyPtr> DirectPropertyMap;
typedef std::map<std::string, PropertyPtr> PropertyMap;
typedef std::map<std::string, ElementPtr> LazyPropertyMap;
/**
* Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
*/
class PropertyTable {
public:
// in-memory property table with no source element
PropertyTable();
PropertyTable(const ElementPtr element, const PropertyTable *templateProps);
~PropertyTable();
PropertyPtr Get(const std::string &name) const;
// PropertyTable's need not be coupled with FBX elements so this can be NULL
const ElementPtr GetElement() const {
return element;
}
const PropertyMap &GetProperties() const {
return props;
}
const LazyPropertyMap &GetLazyProperties() const {
return lazyProps;
}
const PropertyTable *TemplateProps() const {
return templateProps;
}
DirectPropertyMap GetUnparsedProperties() const;
private:
LazyPropertyMap lazyProps;
mutable PropertyMap props;
const PropertyTable *templateProps = nullptr;
const ElementPtr element = nullptr;
};
// ------------------------------------------------------------------------------------------------
template <typename T>
inline T PropertyGet(const PropertyTable *in, const std::string &name, const T &defaultValue) {
PropertyPtr prop = in->Get(name);
if (nullptr == prop) {
return defaultValue;
}
// strong typing, no need to be lenient
const TypedProperty<T> *const tprop = prop->As<TypedProperty<T> >();
if (nullptr == tprop) {
return defaultValue;
}
return tprop->Value();
}
// ------------------------------------------------------------------------------------------------
template <typename T>
inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) {
PropertyPtr prop = in->Get(name);
if (nullptr == prop) {
if (!useTemplate) {
result = false;
return T();
}
const PropertyTable *templ = in->TemplateProps();
if (nullptr == templ) {
result = false;
return T();
}
prop = templ->Get(name);
if (nullptr == prop) {
result = false;
return T();
}
}
// strong typing, no need to be lenient
const TypedProperty<T> *const tprop = prop->As<TypedProperty<T> >();
if (nullptr == tprop) {
result = false;
return T();
}
result = true;
return tprop->Value();
}
} // namespace FBXDocParser
#endif // FBX_PROPERTIES_H

View File

@ -0,0 +1,249 @@
/*************************************************************************/
/* FBXTokenizer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXTokenizer.cpp
* @brief Implementation of the FBX broadphase lexer
*/
// tab width for logging columns
#define ASSIMP_FBX_TAB_WIDTH 4
#include "FBXTokenizer.h"
#include "core/print_string.h"
namespace FBXDocParser {
// ------------------------------------------------------------------------------------------------
Token::Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column) :
sbegin(p_sbegin),
send(p_send),
type(p_type),
line(p_line),
column(p_column) {
#ifdef DEBUG_ENABLED
contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
#endif
}
// ------------------------------------------------------------------------------------------------
Token::~Token() {
}
namespace {
// ------------------------------------------------------------------------------------------------
void TokenizeError(const std::string &message, unsigned int line, unsigned int column) {
print_error("[FBX-Tokenize]" + String(message.c_str()) + " " + itos(line) + ":" + itos(column));
}
// process a potential data token up to 'cur', adding it to 'output_tokens'.
// ------------------------------------------------------------------------------------------------
void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *&end,
unsigned int line,
unsigned int column,
TokenType type = TokenType_DATA,
bool must_have_token = false) {
if (start && end) {
// sanity check:
// tokens should have no whitespace outside quoted text and [start,end] should
// properly delimit the valid range.
bool in_double_quotes = false;
for (const char *c = start; c != end + 1; ++c) {
if (*c == '\"') {
in_double_quotes = !in_double_quotes;
}
if (!in_double_quotes && IsSpaceOrNewLine(*c)) {
TokenizeError("unexpected whitespace in token", line, column);
}
}
if (in_double_quotes) {
TokenizeError("non-terminated double quotes", line, column);
}
output_tokens.push_back(new_Token(start, end + 1, type, line, column));
} else if (must_have_token) {
TokenizeError("unexpected character, expected data token", line, column);
}
start = end = nullptr;
}
} // namespace
// ------------------------------------------------------------------------------------------------
void Tokenize(TokenList &output_tokens, const char *input) {
// line and column numbers numbers are one-based
unsigned int line = 1;
unsigned int column = 1;
bool comment = false;
bool in_double_quotes = false;
bool pending_data_token = false;
const char *token_begin = nullptr, *token_end = nullptr;
for (const char *cur = input; *cur; column += (*cur == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1), ++cur) {
const char c = *cur;
if (IsLineEnd(c)) {
comment = false;
column = 0;
++line;
}
if (comment) {
continue;
}
if (in_double_quotes) {
if (c == '\"') {
in_double_quotes = false;
token_end = cur;
ProcessDataToken(output_tokens, token_begin, token_end, line, column);
pending_data_token = false;
}
continue;
}
switch (c) {
case '\"':
if (token_begin) {
TokenizeError("unexpected double-quote", line, column);
}
token_begin = cur;
in_double_quotes = true;
continue;
case ';':
ProcessDataToken(output_tokens, token_begin, token_end, line, column);
comment = true;
continue;
case '{':
ProcessDataToken(output_tokens, token_begin, token_end, line, column);
output_tokens.push_back(new_Token(cur, cur + 1, TokenType_OPEN_BRACKET, line, column));
continue;
case '}':
ProcessDataToken(output_tokens, token_begin, token_end, line, column);
output_tokens.push_back(new_Token(cur, cur + 1, TokenType_CLOSE_BRACKET, line, column));
continue;
case ',':
if (pending_data_token) {
ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_DATA, true);
}
output_tokens.push_back(new_Token(cur, cur + 1, TokenType_COMMA, line, column));
continue;
case ':':
if (pending_data_token) {
ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_KEY, true);
} else {
TokenizeError("unexpected colon", line, column);
}
continue;
}
if (IsSpaceOrNewLine(c)) {
if (token_begin) {
// peek ahead and check if the next token is a colon in which
// case this counts as KEY token.
TokenType type = TokenType_DATA;
for (const char *peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) {
if (*peek == ':') {
type = TokenType_KEY;
cur = peek;
break;
}
}
ProcessDataToken(output_tokens, token_begin, token_end, line, column, type);
}
pending_data_token = false;
} else {
token_end = cur;
if (!token_begin) {
token_begin = cur;
}
pending_data_token = true;
}
}
}
} // namespace FBXDocParser

View File

@ -0,0 +1,204 @@
/*************************************************************************/
/* FBXTokenizer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXTokenizer.h
* @brief FBX lexer
*/
#ifndef FBX_TOKENIZER_H
#define FBX_TOKENIZER_H
#include "FBXParseTools.h"
#include "core/ustring.h"
#include <iostream>
#include <memory>
#include <string>
#include <vector>
namespace FBXDocParser {
/** Rough classification for text FBX tokens used for constructing the
* basic scope hierarchy. */
enum TokenType {
// {
TokenType_OPEN_BRACKET = 0,
// }
TokenType_CLOSE_BRACKET,
// '"blablubb"', '2', '*14' - very general token class,
// further processing happens at a later stage.
TokenType_DATA,
//
TokenType_BINARY_DATA,
// ,
TokenType_COMMA,
// blubb:
TokenType_KEY
};
/** Represents a single token in a FBX file. Tokens are
* classified by the #TokenType enumerated types.
*
* Offers iterator protocol. Tokens are immutable. */
class Token {
private:
static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1);
public:
/** construct a textual token */
Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column);
/** construct a binary token */
Token(const char *p_sbegin, const char *p_send, TokenType p_type, size_t p_offset);
~Token();
public:
std::string StringContents() const {
return std::string(begin(), end());
}
bool IsBinary() const {
return column == BINARY_MARKER;
}
const char *begin() const {
return sbegin;
}
const char *end() const {
return send;
}
TokenType Type() const {
return type;
}
size_t Offset() const {
return offset;
}
unsigned int Line() const {
return static_cast<unsigned int>(line);
}
unsigned int Column() const {
return column;
}
private:
#ifdef DEBUG_ENABLED
// full string copy for the sole purpose that it nicely appears
// in msvc's debugger window.
std::string contents;
#endif
const char *sbegin = nullptr;
const char *send = nullptr;
const TokenType type;
union {
size_t line;
size_t offset;
};
const unsigned int column = 0;
};
// Fixed leak by using shared_ptr for tokens
typedef Token *TokenPtr;
typedef std::vector<TokenPtr> TokenList;
#define new_Token new Token
/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
*
* Skips over comments and generates line and column numbers.
*
* @param output_tokens Receives a list of all tokens in the input data.
* @param input_buffer Textual input buffer to be processed, 0-terminated.
* @print_error if something goes wrong */
void Tokenize(TokenList &output_tokens, const char *input);
/** Tokenizer function for binary FBX files.
*
* Emits a token list suitable for direct parsing.
*
* @param output_tokens Receives a list of all tokens in the input data.
* @param input_buffer Binary input buffer to be processed.
* @param length Length of input buffer, in bytes. There is no 0-terminal.
* @print_error if something goes wrong */
void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length);
} // namespace FBXDocParser
#endif // FBX_TOKENIZER_H

View File

@ -0,0 +1,221 @@
/*************************************************************************/
/* FBXUtil.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FBXUtil.cpp
* @brief Implementation of internal FBX utility functions
*/
#include "FBXUtil.h"
#include "FBXTokenizer.h"
#include <cstring>
#include <string>
namespace FBXDocParser {
namespace Util {
// ------------------------------------------------------------------------------------------------
const char *TokenTypeString(TokenType t) {
switch (t) {
case TokenType_OPEN_BRACKET:
return "TOK_OPEN_BRACKET";
case TokenType_CLOSE_BRACKET:
return "TOK_CLOSE_BRACKET";
case TokenType_DATA:
return "TOK_DATA";
case TokenType_COMMA:
return "TOK_COMMA";
case TokenType_KEY:
return "TOK_KEY";
case TokenType_BINARY_DATA:
return "TOK_BINARY_DATA";
}
//ai_assert(false);
return "";
}
// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
static const uint8_t base64DecodeTable[128] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255
};
uint8_t DecodeBase64(char ch) {
const auto idx = static_cast<uint8_t>(ch);
if (idx > 127)
return 255;
return base64DecodeTable[idx];
}
size_t ComputeDecodedSizeBase64(const char *in, size_t inLength) {
if (inLength < 2) {
return 0;
}
const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '=');
const size_t full_length = (inLength * 3) >> 2; // div by 4
if (full_length < equals) {
return 0;
}
return full_length - equals;
}
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength) {
if (maxOutLength == 0 || inLength < 2) {
return 0;
}
const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
size_t dst_offset = 0;
int val = 0, valb = -8;
for (size_t src_offset = 0; src_offset < realLength; ++src_offset) {
const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
if (table_value == 255) {
return 0;
}
val = (val << 6) + table_value;
valb += 6;
if (valb >= 0) {
out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF);
valb -= 8;
val &= 0xFFF;
}
}
return dst_offset;
}
static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char EncodeBase64(char byte) {
return to_base64_string[(size_t)byte];
}
/** Encodes a block of 4 bytes to base64 encoding
*
* @param bytes Bytes to encode.
* @param out_string String to write encoded values to.
* @param string_pos Position in out_string.*/
void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) {
char b0 = (bytes[0] & 0xFC) >> 2;
char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4);
char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6);
char b3 = (bytes[2] & 0x3F);
out_string[string_pos + 0] = EncodeBase64(b0);
out_string[string_pos + 1] = EncodeBase64(b1);
out_string[string_pos + 2] = EncodeBase64(b2);
out_string[string_pos + 3] = EncodeBase64(b3);
}
std::string EncodeBase64(const char *data, size_t length) {
// calculate extra bytes needed to get a multiple of 3
size_t extraBytes = 3 - length % 3;
// number of base64 bytes
size_t encodedBytes = 4 * (length + extraBytes) / 3;
std::string encoded_string(encodedBytes, '=');
// read blocks of 3 bytes
for (size_t ib3 = 0; ib3 < length / 3; ib3++) {
const size_t iByte = ib3 * 3;
const size_t iEncodedByte = ib3 * 4;
const char *currData = &data[iByte];
EncodeByteBlock(currData, encoded_string, iEncodedByte);
}
// if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed)
if (extraBytes > 0) {
char finalBytes[4] = { 0, 0, 0, 0 };
memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
const size_t iEncodedByte = encodedBytes - 4;
EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
// add '=' at the end
for (size_t i = 0; i < 4 * extraBytes / 3; i++)
encoded_string[encodedBytes - i - 1] = '=';
}
return encoded_string;
}
} // namespace Util
} // namespace FBXDocParser

View File

@ -1,3 +1,33 @@
/*************************************************************************/
/* FBXUtil.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
@ -43,61 +73,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file FBXUtil.h
* @brief FBX utility functions for internal use
*/
#ifndef INCLUDED_AI_FBX_UTIL_H
#define INCLUDED_AI_FBX_UTIL_H
#ifndef FBX_UTIL_H
#define FBX_UTIL_H
#include "FBXCompileConfig.h"
#include "FBXTokenizer.h"
#include <stdint.h>
namespace Assimp {
namespace FBX {
namespace FBXDocParser {
namespace Util {
/** helper for std::for_each to delete all heap-allocated items in a container */
template<typename T>
struct delete_fun
{
void operator()(const volatile T* del) {
delete del;
}
template <typename T>
struct delete_fun {
void operator()(const volatile T *del) {
delete del;
}
};
/** Get a string representation for a #TokenType. */
const char* TokenTypeString(TokenType t);
/** Format log/error messages using a given offset in the source binary file
*
* @param prefix Message prefix to be preprended to the location info.
* @param text Message text
* @param line Line index, 1-based
* @param column Column index, 1-based
* @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/
std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset);
/** Format log/error messages using a given line location in the source file.
*
* @param prefix Message prefix to be preprended to the location info.
* @param text Message text
* @param line Line index, 1-based
* @param column Column index, 1-based
* @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/
std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column);
/** Format log/error messages using a given cursor token.
*
* @param prefix Message prefix to be preprended to the location info.
* @param text Message text
* @param tok Token where parsing/processing stopped
* @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/
std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok);
const char *TokenTypeString(TokenType t);
/** Decode a single Base64-encoded character.
*
@ -110,7 +105,7 @@ uint8_t DecodeBase64(char ch);
* @param in Characters to decode.
* @param inLength Number of characters to decode.
* @return size of the decoded data (number of bytes)*/
size_t ComputeDecodedSizeBase64(const char* in, size_t inLength);
size_t ComputeDecodedSizeBase64(const char *in, size_t inLength);
/** Decode a Base64-encoded string
*
@ -119,7 +114,7 @@ size_t ComputeDecodedSizeBase64(const char* in, size_t inLength);
* @param out Pointer where we will store the decoded data.
* @param maxOutLength Size of output buffer.
* @return size of the decoded data (number of bytes)*/
size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength);
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength);
char EncodeBase64(char byte);
@ -128,10 +123,9 @@ char EncodeBase64(char byte);
* @param data Binary data to encode.
* @param inLength Number of bytes to encode.
* @return base64-encoded string*/
std::string EncodeBase64(const char* data, size_t length);
std::string EncodeBase64(const char *data, size_t length);
}
}
}
} // namespace Util
} // namespace FBXDocParser
#endif // ! INCLUDED_AI_FBX_UTIL_H
#endif // FBX_UTIL_H

View File

@ -1,6 +1,6 @@
Open Asset Import Library (assimp)
Copyright (c) 2006-2016, assimp team
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -33,8 +33,6 @@ 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.
******************************************************************************
AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
@ -43,7 +41,7 @@ on the internet. They are - unless otherwise stated - copyright of
their respective creators, which may impose additional requirements
on the use of their work. For any of these models, see
<model-name>.source.txt for more legal information. Contact us if you
are a copyright holder and believe that we credited you inproperly or
are a copyright holder and believe that we credited you improperly or
if you don't want your files to appear in the repository.

197
modules/fbx/readme.md Normal file
View File

@ -0,0 +1,197 @@
# Open Source FBX Specification for the Importer
The goal of this document is to make everything in FBX clearly stated, any errors will be corrected over time this
is a first draft.
## fbx parser - originally from assimp
- Folder: /modules/fbx/fbx_parser
- Upstream: assimp
- Original Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019)
- License: BSD-3-Clause
This can never be updated from upstream, we have heavily modified the parser to provide memory safety and add some
functionality. If anything we should give this parser back to assimp at some point as it has a lot of new features.
# Updating assimp fbx parser
Don't. it's not possible the code is rewritten in many areas to remove thirdparty deps and various bugs are fixed.
Many days were put into rewriting the parser to use safe code and safe memory accessors.
# File Headers
FBX Binaries start with the header "Kaydara FBX Binary"
FBX ASCII documents contain a larger header, sometimes with copyright information for a file.
Detecting these is pretty simple.
# What is an OP link?
It's an object to property link. It lists the properties for that object in some cases. Source and destination based by
ID.
# What is a OO link?
Its an object to object link, it contains the ID source and destination ID.
# FBX Node connections
Nodes in FBX are connected using OO links, This means Object to Object.
FBX has a single other kind of link which is Object Property, this is used for Object to Property Links, this can be
extra attributes, defaults, or even some simple settings.
# Bones / Joints / Locators
Bones in FBX are nodes, they initially have the Model:: Type, then have links to SubDeformer the sub deformer
is part of the skin there is also an explicit Skin link, which then links to the geometry using OO links in the
document.
# Rotation Order in FBX compared to Godot
**Godot uses the rotation order:** YXZ
**FBX has dynamic rotation order to prevent gimbal lock with complex animations**
```cpp
enum RotOrder {
RotOrder_EulerXYZ = 0
RotOrder_EulerXZY,
RotOrder_EulerYZX,
RotOrder_EulerYXZ,
RotOrder_EulerZXY,
RotOrder_EulerZYX,
RotOrder_SphericXYZ // nobody uses this - as far as we can tell
};
```
# Pivot transforms
### Pivot description:
- Maya and 3DS max consider everything to be in node space (bones joints, skins, lights, cameras, etc)
- Everything is a node, this means essentially nodes are auto or variants
- They are local to the node in the tree.
- They are used to calculate where a node is in space
```c++
// For a better reference you can check editor_scene_importer_fbx.h
// references: GenFBXTransform / read the data in
// references: ComputePivotTransform / run the calculation
// This is the local pivot transform for the node, not the global transforms
Transform ComputePivotTransform(
Transform chain[TransformationComp_MAXIMUM],
Transform &geometric_transform) {
// Maya pivots
Transform T = chain[TransformationComp_Translation];
Transform Roff = chain[TransformationComp_RotationOffset];
Transform Rp = chain[TransformationComp_RotationPivot];
Transform Rpre = chain[TransformationComp_PreRotation];
Transform R = chain[TransformationComp_Rotation];
Transform Rpost = chain[TransformationComp_PostRotation];
Transform Soff = chain[TransformationComp_ScalingOffset];
Transform Sp = chain[TransformationComp_ScalingPivot];
Transform S = chain[TransformationComp_Scaling];
// 3DS Max Pivots
Transform OT = chain[TransformationComp_GeometricTranslation];
Transform OR = chain[TransformationComp_GeometricRotation];
Transform OS = chain[TransformationComp_GeometricScaling];
// Calculate 3DS max pivot transform - use geometric space (e.g doesn't effect children nodes only the current node)
geometric_transform = OT * OR * OS;
// Calculate standard maya pivots
return T * Roff * Rp * Rpre * R * Rpost.inverse() * Rp.inverse() * Soff * Sp * S * Sp.inverse();
}
```
# Transform inheritance for FBX Nodes
The goal of below is to explain why they implement this in the first place.
The use case is to make nodes have an option to override their local scaling or to make scaling influenced by orientation, which i would imagine would be useful for when you need to rotate a node and the child to scale based on the orientation rather than setting on the rotation matrix planes.
```cpp
// not modified the formatting here since this code must remain clear
enum TransformInheritance {
Transform_RrSs = 0,
// Parent Rotation * Local Rotation * Parent Scale * Local Scale -- Parent Rotation Offset * Parent ScalingOffset (Local scaling is offset by rotation of parent node)
Transform_RSrs = 1, // Parent Rotation * Parent Scale * Local Rotation * Local Scale -- Parent * Local (normal mode)
Transform_Rrs = 2, // Parent Rotation * Local Rotation * Local Scale -- Node transform scale is the only relevant component
TransformInheritance_MAX // end-of-enum sentinel
};
enum TransformInheritance {
Transform_RrSs = 0,
// Local scaling is offset by rotation of parent node
Transform_RSrs = 1,
// Parent * Local (normal mode)
Transform_Rrs = 2,
// Node transform scale is the only relevant component
TransformInheritance_MAX // end-of-enum sentinel
};
```
# Axis in FBX
Godot has one format for the declared axis
AXIS X, AXIS Y, -AXIS Z
FBX supports any format you can think of. As it has to support Maya and 3DS Max.
#### FBX File Header
```json
GlobalSettings: {
Version: 1000
Properties70: {
P: "UpAxis", "int", "Integer", "",1
P: "UpAxisSign", "int", "Integer", "",1
P: "FrontAxis", "int", "Integer", "",2
P: "FrontAxisSign", "int", "Integer", "",1
P: "CoordAxis", "int", "Integer", "",0
P: "CoordAxisSign", "int", "Integer", "",1
P: "OriginalUpAxis", "int", "Integer", "",1
P: "OriginalUpAxisSign", "int", "Integer", "",1
P: "UnitScaleFactor", "double", "Number", "",1
P: "OriginalUnitScaleFactor", "double", "Number", "",1
P: "AmbientColor", "ColorRGB", "Color", "",0,0,0
P: "DefaultCamera", "KString", "", "", "Producer Perspective"
P: "TimeMode", "enum", "", "",6
P: "TimeProtocol", "enum", "", "",2
P: "SnapOnFrameMode", "enum", "", "",0
P: "TimeSpanStart", "KTime", "Time", "",0
P: "TimeSpanStop", "KTime", "Time", "",92372316000
P: "CustomFrameRate", "double", "Number", "",-1
P: "TimeMarker", "Compound", "", ""
P: "CurrentTimeMarker", "int", "Integer", "",-1
}
}
```
#### FBX FILE declares axis dynamically using FBX header
Coord is X
Up is Y
Front is Z
#### GODOT - constant reference point
Coord is X positive,
Y is up positive,
Front is -Z negative
### Explaining MeshGeometry indexing
Reference type declared:
- Direct (directly related to the mapping information type)
- IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
ControlPoint is a vertex
* None The mapping is undetermined.
* ByVertex There will be one mapping coordinate for each surface control point/vertex.
* If you have direct reference type vertices [x]
* If you have IndexToDirect reference type the UV
* ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
* ByPolygon There can be only one mapping coordinate for the whole polygon.
* One mapping per polygon polygon x has this normal x
* For each vertex of the polygon then set the normal to x
* ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
* AllSame There can be only one mapping coordinate for the whole surface.

View File

@ -31,23 +31,23 @@
#include "register_types.h"
#include "editor/editor_node.h"
#include "editor_scene_importer_assimp.h"
#include "editor_scene_importer_fbx.h"
#ifdef TOOLS_ENABLED
static void _editor_init() {
Ref<EditorSceneImporterAssimp> import_assimp;
import_assimp.instance();
ResourceImporterScene::get_singleton()->add_importer(import_assimp);
Ref<EditorSceneImporterFBX> import_fbx;
import_fbx.instance();
ResourceImporterScene::get_singleton()->add_importer(import_fbx);
}
#endif
void register_assimp_types() {
void register_fbx_types() {
#ifdef TOOLS_ENABLED
ClassDB::APIType prev_api = ClassDB::get_current_api();
ClassDB::set_current_api(ClassDB::API_EDITOR);
ClassDB::register_class<EditorSceneImporterAssimp>();
ClassDB::register_class<EditorSceneImporterFBX>();
ClassDB::set_current_api(prev_api);
@ -55,5 +55,5 @@ void register_assimp_types() {
#endif
}
void unregister_assimp_types() {
void unregister_fbx_types() {
}

View File

@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef ASSIMP_REGISTER_TYPES_H
#define ASSIMP_REGISTER_TYPES_H
#ifndef FBX_REGISTER_TYPES_H
#define FBX_REGISTER_TYPES_H
void register_assimp_types();
void unregister_assimp_types();
void register_fbx_types();
void unregister_fbx_types();
#endif // ASSIMP_REGISTER_TYPES_H
#endif // FBX_REGISTER_TYPES_H

View File

@ -0,0 +1,152 @@
/*************************************************************************/
/* import_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "import_utils.h"
Vector3 ImportUtils::deg2rad(const Vector3 &p_rotation) {
return p_rotation / 180.0 * Math_PI;
}
Vector3 ImportUtils::rad2deg(const Vector3 &p_rotation) {
return p_rotation / Math_PI * 180.0;
}
Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) {
Basis ret;
// FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot
// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
switch (mode) {
case FBXDocParser::Model::RotOrder_EulerXYZ:
ret.set_euler_zyx(p_rotation);
break;
case FBXDocParser::Model::RotOrder_EulerXZY:
ret.set_euler_yzx(p_rotation);
break;
case FBXDocParser::Model::RotOrder_EulerYZX:
ret.set_euler_xzy(p_rotation);
break;
case FBXDocParser::Model::RotOrder_EulerYXZ:
ret.set_euler_zxy(p_rotation);
break;
case FBXDocParser::Model::RotOrder_EulerZXY:
ret.set_euler_yxz(p_rotation);
break;
case FBXDocParser::Model::RotOrder_EulerZYX:
ret.set_euler_xyz(p_rotation);
break;
case FBXDocParser::Model::RotOrder_SphericXYZ:
// TODO do this.
break;
default:
// If you land here, Please integrate all enums.
CRASH_NOW_MSG("This is not unreachable.");
}
return ret;
}
Quat ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) {
return ImportUtils::EulerToBasis(mode, p_rotation);
}
Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation) {
// FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot
// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
switch (mode) {
case FBXDocParser::Model::RotOrder_EulerXYZ:
return p_rotation.get_euler_zyx();
case FBXDocParser::Model::RotOrder_EulerXZY:
return p_rotation.get_euler_yzx();
case FBXDocParser::Model::RotOrder_EulerYZX:
return p_rotation.get_euler_xzy();
case FBXDocParser::Model::RotOrder_EulerYXZ:
return p_rotation.get_euler_zxy();
case FBXDocParser::Model::RotOrder_EulerZXY:
return p_rotation.get_euler_yxz();
case FBXDocParser::Model::RotOrder_EulerZYX:
return p_rotation.get_euler_xyz();
case FBXDocParser::Model::RotOrder_SphericXYZ:
// TODO
return Vector3();
default:
// If you land here, Please integrate all enums.
CRASH_NOW_MSG("This is not unreachable.");
return Vector3();
}
}
Vector3 ImportUtils::QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quat &p_rotation) {
return BasisToEuler(mode, p_rotation);
}
Transform get_unscaled_transform(const Transform &p_initial, real_t p_scale) {
Transform unscaled = Transform(p_initial.basis, p_initial.origin * p_scale);
ERR_FAIL_COND_V_MSG(unscaled.basis.determinant() == 0, Transform(), "det is zero unscaled?");
return unscaled;
}
Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices) {
ERR_FAIL_COND_V_MSG(p_vertices.size() < 3, Vector3(0, 0, 0), "At least 3 vertices are necesary");
// Using long double to make sure that normal is computed for even really tiny objects.
typedef long double ldouble;
ldouble x = 0.0;
ldouble y = 0.0;
ldouble z = 0.0;
for (size_t i = 0; i < p_vertices.size(); i += 1) {
const Vector3 current = p_vertices[i];
const Vector3 next = p_vertices[(i + 1) % p_vertices.size()];
x += (ldouble(current.y) - ldouble(next.y)) * (ldouble(current.z) + ldouble(next.z));
y += (ldouble(current.z) - ldouble(next.z)) * (ldouble(current.x) + ldouble(next.x));
z += (ldouble(current.x) - ldouble(next.x)) * (ldouble(current.y) + ldouble(next.y));
}
const ldouble l2 = x * x + y * y + z * z;
if (l2 == 0.0) {
return (p_vertices[0] - p_vertices[1]).normalized().cross((p_vertices[0] - p_vertices[2]).normalized()).normalized();
} else {
const double l = Math::sqrt(double(l2));
return Vector3(x / l, y / l, z / l);
}
}

View File

@ -0,0 +1,386 @@
/*************************************************************************/
/* import_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef IMPORT_UTILS_FBX_IMPORTER_H
#define IMPORT_UTILS_FBX_IMPORTER_H
#include "core/io/image_loader.h"
#include "data/import_state.h"
#include "fbx_parser/FBXDocument.h"
#include <string>
#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
/**
* Import Utils
* Conversion tools / glue code to convert from FBX to Godot
*/
class ImportUtils {
public:
/// Convert a vector from degrees to radians.
static Vector3 deg2rad(const Vector3 &p_rotation);
/// Convert a vector from radians to degrees.
static Vector3 rad2deg(const Vector3 &p_rotation);
/// Converts rotation order vector (in rad) to quaternion.
static Basis EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation);
/// Converts rotation order vector (in rad) to quaternion.
static Quat EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation);
/// Converts basis into rotation order vector (in rad).
static Vector3 BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation);
/// Converts quaternion into rotation order vector (in rad).
static Vector3 QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quat &p_rotation);
static void debug_xform(String name, const Transform &t) {
print_verbose(name + " " + t.origin + " rotation: " + (t.basis.get_euler() * (180 / Math_PI)));
}
static String FBXNodeToName(const std::string &name) {
// strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
// this causes ambiguities, well possible between empty identifiers,
// such as "Model::" and ""). Make sure the behaviour is consistent
// across multiple calls to FixNodeName().
// We must remove this from the name
// Some bones have this
// SubDeformer::
// Meshes, Joints have this, some other IK elements too.
// Model::
String node_name = String(name.c_str());
if (node_name.substr(0, 7) == "Model::") {
node_name = node_name.substr(7, node_name.length() - 7);
return node_name.replace(":", "");
}
if (node_name.substr(0, 13) == "SubDeformer::") {
node_name = node_name.substr(13, node_name.length() - 13);
return node_name.replace(":", "");
}
if (node_name.substr(0, 11) == "AnimStack::") {
node_name = node_name.substr(11, node_name.length() - 11);
return node_name.replace(":", "");
}
if (node_name.substr(0, 15) == "AnimCurveNode::") {
node_name = node_name.substr(15, node_name.length() - 15);
return node_name.replace(":", "");
}
if (node_name.substr(0, 11) == "AnimCurve::") {
node_name = node_name.substr(11, node_name.length() - 11);
return node_name.replace(":", "");
}
if (node_name.substr(0, 10) == "Geometry::") {
node_name = node_name.substr(10, node_name.length() - 10);
return node_name.replace(":", "");
}
if (node_name.substr(0, 10) == "Material::") {
node_name = node_name.substr(10, node_name.length() - 10);
return node_name.replace(":", "");
}
if (node_name.substr(0, 9) == "Texture::") {
node_name = node_name.substr(9, node_name.length() - 9);
return node_name.replace(":", "");
}
return node_name.replace(":", "");
}
static std::string FBXAnimMeshName(const std::string &name) {
if (name.length()) {
size_t indexOf = name.find_first_of("::");
if (indexOf != std::string::npos && indexOf < name.size() - 2) {
return name.substr(indexOf + 2);
}
}
return name.length() ? name : "AnimMesh";
}
static Vector3 safe_import_vector3(const Vector3 &p_vec) {
Vector3 vector = p_vec;
if (Math::is_equal_approx(0, vector.x)) {
vector.x = 0;
}
if (Math::is_equal_approx(0, vector.y)) {
vector.y = 0;
}
if (Math::is_equal_approx(0, vector.z)) {
vector.z = 0;
}
return vector;
}
static void debug_xform(String name, const Basis &t) {
//print_verbose(name + " rotation: " + (t.get_euler() * (180 / Math_PI)));
}
static Vector3 FixAxisConversions(Vector3 input) {
return Vector3(input.x, input.y, input.z);
}
static void AlignMeshAxes(std::vector<Vector3> &vertex_data) {
for (size_t x = 0; x < vertex_data.size(); x++) {
vertex_data[x] = FixAxisConversions(vertex_data[x]);
}
}
struct AssetImportFbx {
enum ETimeMode {
TIME_MODE_DEFAULT = 0,
TIME_MODE_120 = 1,
TIME_MODE_100 = 2,
TIME_MODE_60 = 3,
TIME_MODE_50 = 4,
TIME_MODE_48 = 5,
TIME_MODE_30 = 6,
TIME_MODE_30_DROP = 7,
TIME_MODE_NTSC_DROP_FRAME = 8,
TIME_MODE_NTSC_FULL_FRAME = 9,
TIME_MODE_PAL = 10,
TIME_MODE_CINEMA = 11,
TIME_MODE_1000 = 12,
TIME_MODE_CINEMA_ND = 13,
TIME_MODE_CUSTOM = 14,
TIME_MODE_TIME_MODE_COUNT = 15
};
enum UpAxis {
UP_VECTOR_AXIS_X = 1,
UP_VECTOR_AXIS_Y = 2,
UP_VECTOR_AXIS_Z = 3
};
enum FrontAxis {
FRONT_PARITY_EVEN = 1,
FRONT_PARITY_ODD = 2,
};
enum CoordAxis {
COORD_RIGHT = 0,
COORD_LEFT = 1
};
};
/** Get fbx fps for time mode meta data
*/
static float get_fbx_fps(int32_t time_mode) {
switch (time_mode) {
case AssetImportFbx::TIME_MODE_DEFAULT: return 24;
case AssetImportFbx::TIME_MODE_120: return 120;
case AssetImportFbx::TIME_MODE_100: return 100;
case AssetImportFbx::TIME_MODE_60: return 60;
case AssetImportFbx::TIME_MODE_50: return 50;
case AssetImportFbx::TIME_MODE_48: return 48;
case AssetImportFbx::TIME_MODE_30: return 30;
case AssetImportFbx::TIME_MODE_30_DROP: return 30;
case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
case AssetImportFbx::TIME_MODE_PAL: return 25;
case AssetImportFbx::TIME_MODE_CINEMA: return 24;
case AssetImportFbx::TIME_MODE_1000: return 1000;
case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
case AssetImportFbx::TIME_MODE_CUSTOM: return -1;
}
return 0;
}
static float get_fbx_fps(const FBXDocParser::FileGlobalSettings *FBXSettings) {
int time_mode = FBXSettings->TimeMode();
// get the animation FPS
float frames_per_second = get_fbx_fps(time_mode);
// handle animation custom FPS time.
if (time_mode == ImportUtils::AssetImportFbx::TIME_MODE_CUSTOM) {
print_verbose("FBX Animation has custom FPS setting");
frames_per_second = FBXSettings->CustomFrameRate();
// not our problem this is the modeller, we can print as an error so they can fix the source.
if (frames_per_second == 0) {
print_error("Custom animation time in file is set to 0 value, animation won't play, please edit your file to correct the FPS value");
}
}
return frames_per_second;
}
/**
* Find hardcoded textures from assimp which could be in many different directories
*/
/**
* set_texture_mapping_mode
* Helper to check the mapping mode of the texture (repeat, clamp and mirror)
*/
// static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
// ERR_FAIL_COND(texture.is_null());
// ERR_FAIL_COND(map_mode == NULL);
// aiTextureMapMode tex_mode = map_mode[0];
// int32_t flags = Texture::FLAGS_DEFAULT;
// if (tex_mode == aiTextureMapMode_Wrap) {
// //Default
// } else if (tex_mode == aiTextureMapMode_Clamp) {
// flags = flags & ~Texture::FLAG_REPEAT;
// } else if (tex_mode == aiTextureMapMode_Mirror) {
// flags = flags | Texture::FLAG_MIRRORED_REPEAT;
// }
// texture->set_flags(flags);
// }
/**
* Load or load from cache image :)
* We need to upgrade this in the later version :) should not be hard
*/
//static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path){
// Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
// // if our cache contains this image then don't bother
// if (match) {
// return match->get();
// }
// Vector<String> split_path = p_path.get_basename().split("*");
// if (split_path.size() == 2) {
// size_t texture_idx = split_path[1].to_int();
// ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
// aiTexture *tex = p_scene->mTextures[texture_idx];
// String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
// filename = filename.get_file();
// print_verbose("Open Asset Import: Loading embedded texture " + filename);
// if (tex->mHeight == 0) {
// if (tex->CheckFormat("png")) {
// Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
// ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
// state.path_to_image_cache.insert(p_path, img);
// return img;
// } else if (tex->CheckFormat("jpg")) {
// Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
// ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
// state.path_to_image_cache.insert(p_path, img);
// return img;
// } else if (tex->CheckFormat("dds")) {
// ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented");
// }
// } else {
// Ref<Image> img;
// img.instance();
// PoolByteArray arr;
// uint32_t size = tex->mWidth * tex->mHeight;
// arr.resize(size);
// memcpy(arr.write().ptr(), tex->pcData, size);
// ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
// //ARGB8888 to RGBA8888
// for (int32_t i = 0; i < arr.size() / 4; i++) {
// arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
// arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
// arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
// arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
// }
// img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
// ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
// state.path_to_image_cache.insert(p_path, img);
// return img;
// }
// return Ref<Image>();
// } else {
// Ref<Texture> texture = ResourceLoader::load(p_path);
// ERR_FAIL_COND_V(texture.is_null(), Ref<Image>());
// Ref<Image> image = texture->get_data();
// ERR_FAIL_COND_V(image.is_null(), Ref<Image>());
// state.path_to_image_cache.insert(p_path, image);
// return image;
// }
// return Ref<Image>();
//}
// /* create texture from assimp data, if found in path */
// static bool CreateAssimpTexture(
// AssimpImporter::ImportState &state,
// aiString texture_path,
// String &filename,
// String &path,
// AssimpImageData &image_state) {
// filename = get_raw_string_from_assimp(texture_path);
// path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
// bool found = false;
// find_texture_path(state.path, path, found);
// if (found) {
// image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
// if (image_state.raw_image.is_valid()) {
// image_state.texture.instance();
// image_state.texture->create_from_image(image_state.raw_image);
// image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
// return true;
// }
// }
// return false;
// }
// /** GetAssimpTexture
// * Designed to retrieve textures for you
// */
// static bool GetAssimpTexture(
// AssimpImporter::ImportState &state,
// aiMaterial *ai_material,
// aiTextureType texture_type,
// String &filename,
// String &path,
// AssimpImageData &image_state) {
// aiString ai_filename = aiString();
// if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) {
// return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
// }
// return false;
// }
};
// Apply the transforms so the basis will have scale 1.
Transform get_unscaled_transform(const Transform &p_initial, real_t p_scale);
/// Uses the Newell's method to compute any polygon normal.
/// The polygon must be at least size of 3 or bigger.
Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices);
#endif // IMPORT_UTILS_FBX_IMPORTER_H

18
thirdparty/README.md vendored
View File

@ -1,24 +1,6 @@
# Third party libraries
## assimp
- Upstream: http://github.com/assimp/assimp
- Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019)
- License: BSD-3-Clause
Files extracted from upstream source:
- Run `cmake .` in root folder to generate files
- `code/{CApi,Common,FBX,Material,PostProcessing}/`
- `contrib/utf8cpp/source/`
- `include/`
- `revision.h`
- `CREDITS` and `LICENSE` files
- `rm -f code/Common/ZipArchiveIOSystem.cpp include/assimp/ZipArchiveIOSystem.h
include/assimp/irrXMLWrapper.h`
## bullet
- Upstream: https://github.com/bulletphysics/bullet3

View File

@ -1,156 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 AssimpCExport.cpp
Assimp C export interface. See Exporter.cpp for some notes.
*/
#ifndef ASSIMP_BUILD_NO_EXPORT
#include "CInterfaceIOWrapper.h"
#include <assimp/SceneCombiner.h>
#include "Common/ScenePrivate.h"
#include <assimp/Exporter.hpp>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
ASSIMP_API size_t aiGetExportFormatCount(void)
{
return Exporter().GetExportFormatCount();
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t index)
{
// Note: this is valid as the index always pertains to a built-in exporter,
// for which the returned structure is guaranteed to be of static storage duration.
Exporter exporter;
const aiExportFormatDesc* orig( exporter.GetExportFormatDescription( index ) );
if (NULL == orig) {
return NULL;
}
aiExportFormatDesc *desc = new aiExportFormatDesc;
desc->description = new char[ strlen( orig->description ) + 1 ]();
::strncpy( (char*) desc->description, orig->description, strlen( orig->description ) );
desc->fileExtension = new char[ strlen( orig->fileExtension ) + 1 ]();
::strncpy( ( char* ) desc->fileExtension, orig->fileExtension, strlen( orig->fileExtension ) );
desc->id = new char[ strlen( orig->id ) + 1 ]();
::strncpy( ( char* ) desc->id, orig->id, strlen( orig->id ) );
return desc;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiReleaseExportFormatDescription( const aiExportFormatDesc *desc ) {
if (NULL == desc) {
return;
}
delete [] desc->description;
delete [] desc->fileExtension;
delete [] desc->id;
delete desc;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut)
{
if (!pOut || !pIn) {
return;
}
SceneCombiner::CopyScene(pOut,pIn,true);
ScenePriv(*pOut)->mIsCopy = true;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn)
{
// note: aiReleaseImport() is also able to delete scene copies, but in addition
// it also handles scenes with import metadata.
delete pIn;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing )
{
return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing )
{
Exporter exp;
if (pIO) {
exp.SetIOHandler(new CIOSystemWrapper(pIO));
}
return exp.Export(pScene,pFormatId,pFileName,pPreprocessing);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing )
{
Exporter exp;
if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) {
return NULL;
}
const aiExportDataBlob* blob = exp.GetOrphanedBlob();
ai_assert(blob);
return blob;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData )
{
delete pData;
}
#endif // !ASSIMP_BUILD_NO_EXPORT

View File

@ -1,136 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 aiFileIO -> IOSystem wrapper*/
#include "CInterfaceIOWrapper.h"
namespace Assimp {
CIOStreamWrapper::~CIOStreamWrapper(void)
{
/* Various places depend on this destructor to close the file */
if (mFile) {
mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile);
mFile = nullptr;
}
}
// ...................................................................
size_t CIOStreamWrapper::Read(void* pvBuffer,
size_t pSize,
size_t pCount
){
// need to typecast here as C has no void*
return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount);
}
// ...................................................................
size_t CIOStreamWrapper::Write(const void* pvBuffer,
size_t pSize,
size_t pCount
){
// need to typecast here as C has no void*
return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount);
}
// ...................................................................
aiReturn CIOStreamWrapper::Seek(size_t pOffset,
aiOrigin pOrigin
){
return mFile->SeekProc(mFile,pOffset,pOrigin);
}
// ...................................................................
size_t CIOStreamWrapper::Tell(void) const {
return mFile->TellProc(mFile);
}
// ...................................................................
size_t CIOStreamWrapper::FileSize() const {
return mFile->FileSizeProc(mFile);
}
// ...................................................................
void CIOStreamWrapper::Flush () {
return mFile->FlushProc(mFile);
}
// ------------------------------------------------------------------------------------------------
// Custom IOStream implementation for the C-API
bool CIOSystemWrapper::Exists( const char* pFile) const {
aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,"rb");
if (p){
mFileSystem->CloseProc(mFileSystem,p);
return true;
}
return false;
}
// ...................................................................
char CIOSystemWrapper::getOsSeparator() const {
#ifndef _WIN32
return '/';
#else
return '\\';
#endif
}
// ...................................................................
IOStream* CIOSystemWrapper::Open(const char* pFile,const char* pMode) {
aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode);
if (!p) {
return NULL;
}
return new CIOStreamWrapper(p, this);
}
// ...................................................................
void CIOSystemWrapper::Close( IOStream* pFile) {
if (!pFile) {
return;
}
delete pFile;
}
}

View File

@ -1,99 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 aiFileIO -> IOSystem wrapper*/
#ifndef AI_CIOSYSTEM_H_INCLUDED
#define AI_CIOSYSTEM_H_INCLUDED
#include <assimp/cfileio.h>
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
namespace Assimp {
class CIOSystemWrapper;
// ------------------------------------------------------------------------------------------------
// Custom IOStream implementation for the C-API
class CIOStreamWrapper : public IOStream
{
public:
explicit CIOStreamWrapper(aiFile* pFile, CIOSystemWrapper* io)
: mFile(pFile),
mIO(io)
{}
~CIOStreamWrapper(void);
size_t Read(void* pvBuffer, size_t pSize, size_t pCount);
size_t Write(const void* pvBuffer, size_t pSize, size_t pCount);
aiReturn Seek(size_t pOffset, aiOrigin pOrigin);
size_t Tell(void) const;
size_t FileSize() const;
void Flush();
private:
aiFile* mFile;
CIOSystemWrapper* mIO;
};
class CIOSystemWrapper : public IOSystem
{
friend class CIOStreamWrapper;
public:
explicit CIOSystemWrapper(aiFileIO* pFile)
: mFileSystem(pFile)
{}
bool Exists( const char* pFile) const;
char getOsSeparator() const;
IOStream* Open(const char* pFile,const char* pMode = "rb");
void Close( IOStream* pFile);
private:
aiFileIO* mFileSystem;
};
}
#endif

View File

@ -1,695 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Assimp.cpp
* @brief Implementation of the Plain-C API
*/
#include <assimp/cimport.h>
#include <assimp/LogStream.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
#include <assimp/scene.h>
#include <assimp/GenericProperty.h>
#include <assimp/Exceptional.h>
#include <assimp/BaseImporter.h>
#include "CApi/CInterfaceIOWrapper.h"
#include "Importer.h"
#include "ScenePrivate.h"
#include <list>
// ------------------------------------------------------------------------------------------------
#ifndef ASSIMP_BUILD_SINGLETHREADED
# include <thread>
# include <mutex>
#endif
// ------------------------------------------------------------------------------------------------
using namespace Assimp;
namespace Assimp {
// underlying structure for aiPropertyStore
typedef BatchLoader::PropertyMap PropertyMap;
/** Stores the LogStream objects for all active C log streams */
struct mpred {
bool operator () (const aiLogStream& s0, const aiLogStream& s1) const {
return s0.callback<s1.callback&&s0.user<s1.user;
}
};
typedef std::map<aiLogStream, Assimp::LogStream*, mpred> LogStreamMap;
/** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */
typedef std::list<Assimp::LogStream*> PredefLogStreamMap;
/** Local storage of all active log streams */
static LogStreamMap gActiveLogStreams;
/** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */
static PredefLogStreamMap gPredefinedStreams;
/** Error message of the last failed import process */
static std::string gLastErrorString;
/** Verbose logging active or not? */
static aiBool gVerboseLogging = false;
/** will return all registered importers. */
void GetImporterInstanceList(std::vector< BaseImporter* >& out);
/** will delete all registered importers. */
void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
} // namespace assimp
#ifndef ASSIMP_BUILD_SINGLETHREADED
/** Global mutex to manage the access to the log-stream map */
static std::mutex gLogStreamMutex;
#endif
// ------------------------------------------------------------------------------------------------
// Custom LogStream implementation for the C-API
class LogToCallbackRedirector : public LogStream {
public:
explicit LogToCallbackRedirector(const aiLogStream& s)
: stream (s) {
ai_assert(NULL != s.callback);
}
~LogToCallbackRedirector() {
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(gLogStreamMutex);
#endif
// (HACK) Check whether the 'stream.user' pointer points to a
// custom LogStream allocated by #aiGetPredefinedLogStream.
// In this case, we need to delete it, too. Of course, this
// might cause strange problems, but the chance is quite low.
PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(),
gPredefinedStreams.end(), (Assimp::LogStream*)stream.user);
if (it != gPredefinedStreams.end()) {
delete *it;
gPredefinedStreams.erase(it);
}
}
/** @copydoc LogStream::write */
void write(const char* message) {
stream.callback(message,stream.user);
}
private:
aiLogStream stream;
};
// ------------------------------------------------------------------------------------------------
void ReportSceneNotFoundError() {
ASSIMP_LOG_ERROR("Unable to find the Assimp::Importer for this aiScene. "
"The C-API does not accept scenes produced by the C++ API and vice versa");
ai_assert(false);
}
// ------------------------------------------------------------------------------------------------
// Reads the given file and returns its content.
const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) {
return aiImportFileEx(pFile,pFlags,NULL);
}
// ------------------------------------------------------------------------------------------------
const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) {
return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL);
}
// ------------------------------------------------------------------------------------------------
const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags,
aiFileIO* pFS, const aiPropertyStore* props) {
ai_assert(NULL != pFile);
const aiScene* scene = NULL;
ASSIMP_BEGIN_EXCEPTION_REGION();
// create an Importer for this file
Assimp::Importer* imp = new Assimp::Importer();
// copy properties
if(props) {
const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
ImporterPimpl* pimpl = imp->Pimpl();
pimpl->mIntProperties = pp->ints;
pimpl->mFloatProperties = pp->floats;
pimpl->mStringProperties = pp->strings;
pimpl->mMatrixProperties = pp->matrices;
}
// setup a custom IO system if necessary
if (pFS) {
imp->SetIOHandler( new CIOSystemWrapper (pFS) );
}
// and have it read the file
scene = imp->ReadFile( pFile, pFlags);
// if succeeded, store the importer in the scene and keep it alive
if( scene) {
ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
priv->mOrigImporter = imp;
} else {
// if failed, extract error code and destroy the import
gLastErrorString = imp->GetErrorString();
delete imp;
}
// return imported data. If the import failed the pointer is NULL anyways
ASSIMP_END_EXCEPTION_REGION(const aiScene*);
return scene;
}
// ------------------------------------------------------------------------------------------------
const aiScene* aiImportFileFromMemory(
const char* pBuffer,
unsigned int pLength,
unsigned int pFlags,
const char* pHint)
{
return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL);
}
// ------------------------------------------------------------------------------------------------
const aiScene* aiImportFileFromMemoryWithProperties(
const char* pBuffer,
unsigned int pLength,
unsigned int pFlags,
const char* pHint,
const aiPropertyStore* props)
{
ai_assert( NULL != pBuffer );
ai_assert( 0 != pLength );
const aiScene* scene = NULL;
ASSIMP_BEGIN_EXCEPTION_REGION();
// create an Importer for this file
Assimp::Importer* imp = new Assimp::Importer();
// copy properties
if(props) {
const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
ImporterPimpl* pimpl = imp->Pimpl();
pimpl->mIntProperties = pp->ints;
pimpl->mFloatProperties = pp->floats;
pimpl->mStringProperties = pp->strings;
pimpl->mMatrixProperties = pp->matrices;
}
// and have it read the file from the memory buffer
scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint);
// if succeeded, store the importer in the scene and keep it alive
if( scene) {
ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
priv->mOrigImporter = imp;
}
else {
// if failed, extract error code and destroy the import
gLastErrorString = imp->GetErrorString();
delete imp;
}
// return imported data. If the import failed the pointer is NULL anyways
ASSIMP_END_EXCEPTION_REGION(const aiScene*);
return scene;
}
// ------------------------------------------------------------------------------------------------
// Releases all resources associated with the given import process.
void aiReleaseImport( const aiScene* pScene)
{
if (!pScene) {
return;
}
ASSIMP_BEGIN_EXCEPTION_REGION();
// find the importer associated with this data
const ScenePrivateData* priv = ScenePriv(pScene);
if( !priv || !priv->mOrigImporter) {
delete pScene;
}
else {
// deleting the Importer also deletes the scene
// Note: the reason that this is not written as 'delete priv->mOrigImporter'
// is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339)
Importer* importer = priv->mOrigImporter;
delete importer;
}
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene,
unsigned int pFlags)
{
const aiScene* sc = NULL;
ASSIMP_BEGIN_EXCEPTION_REGION();
// find the importer associated with this data
const ScenePrivateData* priv = ScenePriv(pScene);
if( !priv || !priv->mOrigImporter) {
ReportSceneNotFoundError();
return NULL;
}
sc = priv->mOrigImporter->ApplyPostProcessing(pFlags);
if (!sc) {
aiReleaseImport(pScene);
return NULL;
}
ASSIMP_END_EXCEPTION_REGION(const aiScene*);
return sc;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API const aiScene *aiApplyCustomizedPostProcessing( const aiScene *scene,
BaseProcess* process,
bool requestValidation ) {
const aiScene* sc( NULL );
ASSIMP_BEGIN_EXCEPTION_REGION();
// find the importer associated with this data
const ScenePrivateData* priv = ScenePriv( scene );
if ( NULL == priv || NULL == priv->mOrigImporter ) {
ReportSceneNotFoundError();
return NULL;
}
sc = priv->mOrigImporter->ApplyCustomizedPostProcessing( process, requestValidation );
if ( !sc ) {
aiReleaseImport( scene );
return NULL;
}
ASSIMP_END_EXCEPTION_REGION( const aiScene* );
return sc;
}
// ------------------------------------------------------------------------------------------------
void CallbackToLogRedirector (const char* msg, char* dt)
{
ai_assert( NULL != msg );
ai_assert( NULL != dt );
LogStream* s = (LogStream*)dt;
s->write(msg);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file)
{
aiLogStream sout;
ASSIMP_BEGIN_EXCEPTION_REGION();
LogStream* stream = LogStream::createDefaultStream(pStream,file);
if (!stream) {
sout.callback = NULL;
sout.user = NULL;
}
else {
sout.callback = &CallbackToLogRedirector;
sout.user = (char*)stream;
}
gPredefinedStreams.push_back(stream);
ASSIMP_END_EXCEPTION_REGION(aiLogStream);
return sout;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiAttachLogStream( const aiLogStream* stream )
{
ASSIMP_BEGIN_EXCEPTION_REGION();
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(gLogStreamMutex);
#endif
LogStream* lg = new LogToCallbackRedirector(*stream);
gActiveLogStreams[*stream] = lg;
if (DefaultLogger::isNullLogger()) {
DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
}
DefaultLogger::get()->attachStream(lg);
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream)
{
ASSIMP_BEGIN_EXCEPTION_REGION();
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(gLogStreamMutex);
#endif
// find the log-stream associated with this data
LogStreamMap::iterator it = gActiveLogStreams.find( *stream);
// it should be there... else the user is playing fools with us
if( it == gActiveLogStreams.end()) {
return AI_FAILURE;
}
DefaultLogger::get()->detatchStream( it->second );
delete it->second;
gActiveLogStreams.erase( it);
if (gActiveLogStreams.empty()) {
DefaultLogger::kill();
}
ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_SUCCESS;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiDetachAllLogStreams(void)
{
ASSIMP_BEGIN_EXCEPTION_REGION();
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(gLogStreamMutex);
#endif
Logger *logger( DefaultLogger::get() );
if ( NULL == logger ) {
return;
}
for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) {
logger->detatchStream( it->second );
delete it->second;
}
gActiveLogStreams.clear();
DefaultLogger::kill();
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiEnableVerboseLogging(aiBool d)
{
if (!DefaultLogger::isNullLogger()) {
DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
}
gVerboseLogging = d;
}
// ------------------------------------------------------------------------------------------------
// Returns the error text of the last failed import process.
const char* aiGetErrorString()
{
return gLastErrorString.c_str();
}
// -----------------------------------------------------------------------------------------------
// Return the description of a importer given its index
const aiImporterDesc* aiGetImportFormatDescription( size_t pIndex)
{
return Importer().GetImporterInfo(pIndex);
}
// -----------------------------------------------------------------------------------------------
// Return the number of importers
size_t aiGetImportFormatCount(void)
{
return Importer().GetImporterCount();
}
// ------------------------------------------------------------------------------------------------
// Returns the error text of the last failed import process.
aiBool aiIsExtensionSupported(const char* szExtension)
{
ai_assert(NULL != szExtension);
aiBool candoit=AI_FALSE;
ASSIMP_BEGIN_EXCEPTION_REGION();
// FIXME: no need to create a temporary Importer instance just for that ..
Assimp::Importer tmp;
candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE;
ASSIMP_END_EXCEPTION_REGION(aiBool);
return candoit;
}
// ------------------------------------------------------------------------------------------------
// Get a list of all file extensions supported by ASSIMP
void aiGetExtensionList(aiString* szOut)
{
ai_assert(NULL != szOut);
ASSIMP_BEGIN_EXCEPTION_REGION();
// FIXME: no need to create a temporary Importer instance just for that ..
Assimp::Importer tmp;
tmp.GetExtensionList(*szOut);
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
// Get the memory requirements for a particular import.
void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
C_STRUCT aiMemoryInfo* in)
{
ASSIMP_BEGIN_EXCEPTION_REGION();
// find the importer associated with this data
const ScenePrivateData* priv = ScenePriv(pIn);
if( !priv || !priv->mOrigImporter) {
ReportSceneNotFoundError();
return;
}
return priv->mOrigImporter->GetMemoryRequirements(*in);
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void)
{
return reinterpret_cast<aiPropertyStore*>( new PropertyMap() );
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p)
{
delete reinterpret_cast<PropertyMap*>(p);
}
// ------------------------------------------------------------------------------------------------
// Importer::SetPropertyInteger
ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value)
{
ASSIMP_BEGIN_EXCEPTION_REGION();
PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
SetGenericProperty<int>(pp->ints,szName,value);
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
// Importer::SetPropertyFloat
ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, ai_real value)
{
ASSIMP_BEGIN_EXCEPTION_REGION();
PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
SetGenericProperty<ai_real>(pp->floats,szName,value);
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
// Importer::SetPropertyString
ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName,
const C_STRUCT aiString* st)
{
if (!st) {
return;
}
ASSIMP_BEGIN_EXCEPTION_REGION();
PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
SetGenericProperty<std::string>(pp->strings,szName,std::string(st->C_Str()));
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
// Importer::SetPropertyMatrix
ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName,
const C_STRUCT aiMatrix4x4* mat)
{
if (!mat) {
return;
}
ASSIMP_BEGIN_EXCEPTION_REGION();
PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
SetGenericProperty<aiMatrix4x4>(pp->matrices,szName,*mat);
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
// Rotation matrix to quaternion
ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat)
{
ai_assert( NULL != quat );
ai_assert( NULL != mat );
*quat = aiQuaternion(*mat);
}
// ------------------------------------------------------------------------------------------------
// Matrix decomposition
ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling,
aiQuaternion* rotation,
aiVector3D* position)
{
ai_assert( NULL != rotation );
ai_assert( NULL != position );
ai_assert( NULL != scaling );
ai_assert( NULL != mat );
mat->Decompose(*scaling,*rotation,*position);
}
// ------------------------------------------------------------------------------------------------
// Matrix transpose
ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat)
{
ai_assert(NULL != mat);
mat->Transpose();
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat)
{
ai_assert(NULL != mat);
mat->Transpose();
}
// ------------------------------------------------------------------------------------------------
// Vector transformation
ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec,
const aiMatrix3x3* mat)
{
ai_assert( NULL != mat );
ai_assert( NULL != vec);
*vec *= (*mat);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec,
const aiMatrix4x4* mat)
{
ai_assert( NULL != mat );
ai_assert( NULL != vec );
*vec *= (*mat);
}
// ------------------------------------------------------------------------------------------------
// Matrix multiplication
ASSIMP_API void aiMultiplyMatrix4(
aiMatrix4x4* dst,
const aiMatrix4x4* src)
{
ai_assert( NULL != dst );
ai_assert( NULL != src );
*dst = (*dst) * (*src);
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiMultiplyMatrix3(
aiMatrix3x3* dst,
const aiMatrix3x3* src)
{
ai_assert( NULL != dst );
ai_assert( NULL != src );
*dst = (*dst) * (*src);
}
// ------------------------------------------------------------------------------------------------
// Matrix identity
ASSIMP_API void aiIdentityMatrix3(
aiMatrix3x3* mat)
{
ai_assert(NULL != mat);
*mat = aiMatrix3x3();
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API void aiIdentityMatrix4(
aiMatrix4x4* mat)
{
ai_assert(NULL != mat);
*mat = aiMatrix4x4();
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API C_STRUCT const aiImporterDesc* aiGetImporterDesc( const char *extension ) {
if( NULL == extension ) {
return NULL;
}
const aiImporterDesc *desc( NULL );
std::vector< BaseImporter* > out;
GetImporterInstanceList( out );
for( size_t i = 0; i < out.size(); ++i ) {
if( 0 == strncmp( out[ i ]->GetInfo()->mFileExtensions, extension, strlen( extension ) ) ) {
desc = out[ i ]->GetInfo();
break;
}
}
DeleteImporterInstanceList(out);
return desc;
}
// ------------------------------------------------------------------------------------------------

View File

@ -1,656 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 BaseImporter.cpp
* @brief Implementation of BaseImporter
*/
#include <assimp/BaseImporter.h>
#include <assimp/ParsingUtils.h>
#include "FileSystemFilter.h"
#include "Importer.h"
#include <assimp/ByteSwapper.h>
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/importerdesc.h>
#include <ios>
#include <list>
#include <memory>
#include <sstream>
#include <cctype>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
BaseImporter::BaseImporter() AI_NO_EXCEPT
: m_progress() {
/**
* Assimp Importer
* unit conversions available
* if you need another measurment unit add it below.
* it's currently defined in assimp that we prefer meters.
*
* NOTE: Initialised here rather than in the header file
* to workaround a VS2013 bug with brace initialisers
* */
importerUnits[ImporterUnits::M] = 1.0;
importerUnits[ImporterUnits::CM] = 0.01;
importerUnits[ImporterUnits::MM] = 0.001;
importerUnits[ImporterUnits::INCHES] = 0.0254;
importerUnits[ImporterUnits::FEET] = 0.3048;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
BaseImporter::~BaseImporter() {
// nothing to do here
}
void BaseImporter::UpdateImporterScale( Importer* pImp )
{
ai_assert(pImp != nullptr);
ai_assert(importerScale != 0.0);
ai_assert(fileScale != 0.0);
double activeScale = importerScale * fileScale;
// Set active scaling
pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, static_cast<float>( activeScale) );
ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
}
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
m_progress = pImp->GetProgressHandler();
if (nullptr == m_progress) {
return nullptr;
}
ai_assert(m_progress);
// Gather configuration properties for this run
SetupProperties( pImp );
// Construct a file system filter to improve our success ratio at reading external files
FileSystemFilter filter(pFile,pIOHandler);
// create a scene object to hold the data
std::unique_ptr<aiScene> sc(new aiScene());
// dispatch importing
try
{
InternReadFile( pFile, sc.get(), &filter);
// Calculate import scale hook - required because pImp not available anywhere else
// passes scale into ScaleProcess
UpdateImporterScale(pImp);
} catch( const std::exception& err ) {
// extract error description
m_ErrorText = err.what();
ASSIMP_LOG_ERROR(m_ErrorText);
return nullptr;
}
// return what we gathered from the import.
return sc.release();
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::SetupProperties(const Importer* pImp)
{
// the default implementation does nothing
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
const aiImporterDesc* desc = GetInfo();
ai_assert(desc != nullptr);
const char* ext = desc->mFileExtensions;
ai_assert(ext != nullptr );
const char* last = ext;
do {
if (!*ext || *ext == ' ') {
extensions.insert(std::string(last,ext-last));
ai_assert(ext-last > 0);
last = ext;
while(*last == ' ') {
++last;
}
}
}
while(*ext++);
}
// ------------------------------------------------------------------------------------------------
/*static*/ bool BaseImporter::SearchFileHeaderForToken( IOSystem* pIOHandler,
const std::string& pFile,
const char** tokens,
unsigned int numTokens,
unsigned int searchBytes /* = 200 */,
bool tokensSol /* false */,
bool noAlphaBeforeTokens /* false */)
{
ai_assert( nullptr != tokens );
ai_assert( 0 != numTokens );
ai_assert( 0 != searchBytes);
if ( nullptr == pIOHandler ) {
return false;
}
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile));
if (pStream.get() ) {
// read 200 characters from the file
std::unique_ptr<char[]> _buffer (new char[searchBytes+1 /* for the '\0' */]);
char *buffer( _buffer.get() );
const size_t read( pStream->Read(buffer,1,searchBytes) );
if( 0 == read ) {
return false;
}
for( size_t i = 0; i < read; ++i ) {
buffer[ i ] = static_cast<char>( ::tolower( buffer[ i ] ) );
}
// It is not a proper handling of unicode files here ...
// ehm ... but it works in most cases.
char* cur = buffer,*cur2 = buffer,*end = &buffer[read];
while (cur != end) {
if( *cur ) {
*cur2++ = *cur;
}
++cur;
}
*cur2 = '\0';
std::string token;
for (unsigned int i = 0; i < numTokens; ++i ) {
ai_assert( nullptr != tokens[i] );
const size_t len( strlen( tokens[ i ] ) );
token.clear();
const char *ptr( tokens[ i ] );
for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) {
token.push_back( static_cast<char>( tolower( *ptr ) ) );
++ptr;
}
const char* r = strstr( buffer, token.c_str() );
if( !r ) {
continue;
}
// We need to make sure that we didn't accidentially identify the end of another token as our token,
// e.g. in a previous version the "gltf " present in some gltf files was detected as "f "
if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) {
continue;
}
// We got a match, either we don't care where it is, or it happens to
// be in the beginning of the file / line
if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: ", tokens[i] );
return true;
}
}
}
return false;
}
// ------------------------------------------------------------------------------------------------
// Simple check for file extension
/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile,
const char* ext0,
const char* ext1,
const char* ext2)
{
std::string::size_type pos = pFile.find_last_of('.');
// no file extension - can't read
if( pos == std::string::npos)
return false;
const char* ext_real = & pFile[ pos+1 ];
if( !ASSIMP_stricmp(ext_real,ext0) )
return true;
// check for other, optional, file extensions
if (ext1 && !ASSIMP_stricmp(ext_real,ext1))
return true;
if (ext2 && !ASSIMP_stricmp(ext_real,ext2))
return true;
return false;
}
// ------------------------------------------------------------------------------------------------
// Get file extension from path
std::string BaseImporter::GetExtension( const std::string& file ) {
std::string::size_type pos = file.find_last_of('.');
// no file extension at all
if (pos == std::string::npos) {
return "";
}
// thanks to Andy Maloney for the hint
std::string ret = file.substr( pos + 1 );
std::transform( ret.begin(), ret.end(), ret.begin(), ToLower<char>);
return ret;
}
// ------------------------------------------------------------------------------------------------
// Check for magic bytes at the beginning of the file.
/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
const void* _magic, unsigned int num, unsigned int offset, unsigned int size)
{
ai_assert( size <= 16 );
ai_assert( _magic );
if (!pIOHandler) {
return false;
}
union {
const char* magic;
const uint16_t* magic_u16;
const uint32_t* magic_u32;
};
magic = reinterpret_cast<const char*>(_magic);
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile));
if (pStream.get() ) {
// skip to offset
pStream->Seek(offset,aiOrigin_SET);
// read 'size' characters from the file
union {
char data[16];
uint16_t data_u16[8];
uint32_t data_u32[4];
};
if(size != pStream->Read(data,1,size)) {
return false;
}
for (unsigned int i = 0; i < num; ++i) {
// also check against big endian versions of tokens with size 2,4
// that's just for convenience, the chance that we cause conflicts
// is quite low and it can save some lines and prevent nasty bugs
if (2 == size) {
uint16_t rev = *magic_u16;
ByteSwap::Swap(&rev);
if (data_u16[0] == *magic_u16 || data_u16[0] == rev) {
return true;
}
}
else if (4 == size) {
uint32_t rev = *magic_u32;
ByteSwap::Swap(&rev);
if (data_u32[0] == *magic_u32 || data_u32[0] == rev) {
return true;
}
}
else {
// any length ... just compare
if(!memcmp(magic,data,size)) {
return true;
}
}
magic += size;
}
}
return false;
}
#ifdef ASSIMP_USE_HUNTER
# include <utf8/utf8.h>
#else
# include "../contrib/utf8cpp/source/utf8.h"
#endif
// ------------------------------------------------------------------------------------------------
// Convert to UTF8 data
void BaseImporter::ConvertToUTF8(std::vector<char>& data)
{
//ConversionResult result;
if(data.size() < 8) {
throw DeadlyImportError("File is too small");
}
// UTF 8 with BOM
if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
ASSIMP_LOG_DEBUG("Found UTF-8 BOM ...");
std::copy(data.begin()+3,data.end(),data.begin());
data.resize(data.size()-3);
return;
}
// UTF 32 BE with BOM
if(*((uint32_t*)&data.front()) == 0xFFFE0000) {
// swap the endianness ..
for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) {
AI_SWAP4P(p);
}
}
// UTF 32 LE with BOM
if(*((uint32_t*)&data.front()) == 0x0000FFFE) {
ASSIMP_LOG_DEBUG("Found UTF-32 BOM ...");
std::vector<char> output;
int *ptr = (int*)&data[ 0 ];
int *end = ptr + ( data.size() / sizeof(int) ) +1;
utf8::utf32to8( ptr, end, back_inserter(output));
return;
}
// UTF 16 BE with BOM
if(*((uint16_t*)&data.front()) == 0xFFFE) {
// swap the endianness ..
for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) {
ByteSwap::Swap2(p);
}
}
// UTF 16 LE with BOM
if(*((uint16_t*)&data.front()) == 0xFEFF) {
ASSIMP_LOG_DEBUG("Found UTF-16 BOM ...");
std::vector<unsigned char> output;
utf8::utf16to8(data.begin(), data.end(), back_inserter(output));
return;
}
}
// ------------------------------------------------------------------------------------------------
// Convert to UTF8 data to ISO-8859-1
void BaseImporter::ConvertUTF8toISO8859_1(std::string& data)
{
size_t size = data.size();
size_t i = 0, j = 0;
while(i < size) {
if ((unsigned char) data[i] < (size_t) 0x80) {
data[j] = data[i];
} else if(i < size - 1) {
if((unsigned char) data[i] == 0xC2) {
data[j] = data[++i];
} else if((unsigned char) data[i] == 0xC3) {
data[j] = ((unsigned char) data[++i] + 0x40);
} else {
std::stringstream stream;
stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1.";
ASSIMP_LOG_ERROR( stream.str() );
data[j++] = data[i++];
data[j] = data[i];
}
} else {
ASSIMP_LOG_ERROR("UTF8 code but only one character remaining");
data[j] = data[i];
}
i++; j++;
}
data.resize(j);
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::TextFileToBuffer(IOStream* stream,
std::vector<char>& data,
TextFileMode mode)
{
ai_assert(nullptr != stream);
const size_t fileSize = stream->FileSize();
if (mode == FORBID_EMPTY) {
if(!fileSize) {
throw DeadlyImportError("File is empty");
}
}
data.reserve(fileSize+1);
data.resize(fileSize);
if(fileSize > 0) {
if(fileSize != stream->Read( &data[0], 1, fileSize)) {
throw DeadlyImportError("File read error");
}
ConvertToUTF8(data);
}
// append a binary zero to simplify string parsing
data.push_back(0);
}
// ------------------------------------------------------------------------------------------------
namespace Assimp {
// Represents an import request
struct LoadRequest {
LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id)
: file(_file)
, flags(_flags)
, refCnt(1)
, scene(NULL)
, loaded(false)
, id(_id) {
if ( _map ) {
map = *_map;
}
}
bool operator== ( const std::string& f ) const {
return file == f;
}
const std::string file;
unsigned int flags;
unsigned int refCnt;
aiScene *scene;
bool loaded;
BatchLoader::PropertyMap map;
unsigned int id;
};
}
// ------------------------------------------------------------------------------------------------
// BatchLoader::pimpl data structure
struct Assimp::BatchData {
BatchData( IOSystem* pIO, bool validate )
: pIOSystem( pIO )
, pImporter( nullptr )
, next_id(0xffff)
, validate( validate ) {
ai_assert( nullptr != pIO );
pImporter = new Importer();
pImporter->SetIOHandler( pIO );
}
~BatchData() {
pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */
delete pImporter;
}
// IO system to be used for all imports
IOSystem* pIOSystem;
// Importer used to load all meshes
Importer* pImporter;
// List of all imports
std::list<LoadRequest> requests;
// Base path
std::string pathBase;
// Id for next item
unsigned int next_id;
// Validation enabled state
bool validate;
};
typedef std::list<LoadRequest>::iterator LoadReqIt;
// ------------------------------------------------------------------------------------------------
BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) {
ai_assert(nullptr != pIO);
m_data = new BatchData( pIO, validate );
}
// ------------------------------------------------------------------------------------------------
BatchLoader::~BatchLoader()
{
// delete all scenes what have not been polled by the user
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
delete (*it).scene;
}
delete m_data;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::setValidation( bool enabled ) {
m_data->validate = enabled;
}
// ------------------------------------------------------------------------------------------------
bool BatchLoader::getValidation() const {
return m_data->validate;
}
// ------------------------------------------------------------------------------------------------
unsigned int BatchLoader::AddLoadRequest(const std::string& file,
unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
{
ai_assert(!file.empty());
// check whether we have this loading request already
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
// Call IOSystem's path comparison function here
if ( m_data->pIOSystem->ComparePaths((*it).file,file)) {
if (map) {
if ( !( ( *it ).map == *map ) ) {
continue;
}
}
else if ( !( *it ).map.empty() ) {
continue;
}
(*it).refCnt++;
return (*it).id;
}
}
// no, we don't have it. So add it to the queue ...
m_data->requests.push_back(LoadRequest(file,steps,map, m_data->next_id));
return m_data->next_id++;
}
// ------------------------------------------------------------------------------------------------
aiScene* BatchLoader::GetImport( unsigned int which )
{
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
if ((*it).id == which && (*it).loaded) {
aiScene* sc = (*it).scene;
if (!(--(*it).refCnt)) {
m_data->requests.erase(it);
}
return sc;
}
}
return nullptr;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::LoadAll()
{
// no threaded implementation for the moment
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
// force validation in debug builds
unsigned int pp = (*it).flags;
if ( m_data->validate ) {
pp |= aiProcess_ValidateDataStructure;
}
// setup config properties if necessary
ImporterPimpl* pimpl = m_data->pImporter->Pimpl();
pimpl->mFloatProperties = (*it).map.floats;
pimpl->mIntProperties = (*it).map.ints;
pimpl->mStringProperties = (*it).map.strings;
pimpl->mMatrixProperties = (*it).map.matrices;
if (!DefaultLogger::isNullLogger())
{
ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%");
ASSIMP_LOG_INFO_F("File: ", (*it).file);
}
m_data->pImporter->ReadFile((*it).file,pp);
(*it).scene = m_data->pImporter->GetOrphanedScene();
(*it).loaded = true;
ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%");
}
}

View File

@ -1,107 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Implementation of BaseProcess */
#include <assimp/BaseImporter.h>
#include "BaseProcess.h"
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include "Importer.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
BaseProcess::BaseProcess() AI_NO_EXCEPT
: shared()
, progress()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
BaseProcess::~BaseProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
void BaseProcess::ExecuteOnScene( Importer* pImp)
{
ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene);
progress = pImp->GetProgressHandler();
ai_assert(progress);
SetupProperties( pImp );
// catch exceptions thrown inside the PostProcess-Step
try
{
Execute(pImp->Pimpl()->mScene);
} catch( const std::exception& err ) {
// extract error description
pImp->Pimpl()->mErrorString = err.what();
ASSIMP_LOG_ERROR(pImp->Pimpl()->mErrorString);
// and kill the partially imported data
delete pImp->Pimpl()->mScene;
pImp->Pimpl()->mScene = nullptr;
}
}
// ------------------------------------------------------------------------------------------------
void BaseProcess::SetupProperties(const Importer* /*pImp*/)
{
// the default implementation does nothing
}
// ------------------------------------------------------------------------------------------------
bool BaseProcess::RequireVerboseFormat() const
{
return true;
}

View File

@ -1,290 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Base class of all import post processing steps */
#ifndef INCLUDED_AI_BASEPROCESS_H
#define INCLUDED_AI_BASEPROCESS_H
#include <map>
#include <assimp/GenericProperty.h>
struct aiScene;
namespace Assimp {
class Importer;
// ---------------------------------------------------------------------------
/** Helper class to allow post-processing steps to interact with each other.
*
* The class maintains a simple property list that can be used by pp-steps
* to provide additional information to other steps. This is primarily
* intended for cross-step optimizations.
*/
class SharedPostProcessInfo
{
public:
struct Base
{
virtual ~Base()
{}
};
//! Represents data that is allocated on the heap, thus needs to be deleted
template <typename T>
struct THeapData : public Base
{
explicit THeapData(T* in)
: data (in)
{}
~THeapData()
{
delete data;
}
T* data;
};
//! Represents static, by-value data not allocated on the heap
template <typename T>
struct TStaticData : public Base
{
explicit TStaticData(T in)
: data (in)
{}
~TStaticData()
{}
T data;
};
// some typedefs for cleaner code
typedef unsigned int KeyType;
typedef std::map<KeyType, Base*> PropertyMap;
public:
//! Destructor
~SharedPostProcessInfo()
{
Clean();
}
//! Remove all stored properties from the table
void Clean()
{
// invoke the virtual destructor for all stored properties
for (PropertyMap::iterator it = pmap.begin(), end = pmap.end();
it != end; ++it)
{
delete (*it).second;
}
pmap.clear();
}
//! Add a heap property to the list
template <typename T>
void AddProperty( const char* name, T* in ){
AddProperty(name,(Base*)new THeapData<T>(in));
}
//! Add a static by-value property to the list
template <typename T>
void AddProperty( const char* name, T in ){
AddProperty(name,(Base*)new TStaticData<T>(in));
}
//! Get a heap property
template <typename T>
bool GetProperty( const char* name, T*& out ) const
{
THeapData<T>* t = (THeapData<T>*)GetPropertyInternal(name);
if(!t)
{
out = NULL;
return false;
}
out = t->data;
return true;
}
//! Get a static, by-value property
template <typename T>
bool GetProperty( const char* name, T& out ) const
{
TStaticData<T>* t = (TStaticData<T>*)GetPropertyInternal(name);
if(!t)return false;
out = t->data;
return true;
}
//! Remove a property of a specific type
void RemoveProperty( const char* name) {
SetGenericPropertyPtr<Base>(pmap,name,NULL);
}
private:
void AddProperty( const char* name, Base* data) {
SetGenericPropertyPtr<Base>(pmap,name,data);
}
Base* GetPropertyInternal( const char* name) const {
return GetGenericProperty<Base*>(pmap,name,NULL);
}
private:
//! Map of all stored properties
PropertyMap pmap;
};
#if 0
// ---------------------------------------------------------------------------
/** @brief Represents a dependency table for a postprocessing steps.
*
* For future use.
*/
struct PPDependencyTable
{
unsigned int execute_me_before_these;
unsigned int execute_me_after_these;
unsigned int only_if_these_are_not_specified;
unsigned int mutually_exclusive_with;
};
#endif
#define AI_SPP_SPATIAL_SORT "$Spat"
// ---------------------------------------------------------------------------
/** The BaseProcess defines a common interface for all post processing steps.
* A post processing step is run after a successful import if the caller
* specified the corresponding flag when calling ReadFile().
* Enum #aiPostProcessSteps defines which flags are available.
* After a successful import the Importer iterates over its internal array
* of processes and calls IsActive() on each process to evaluate if the step
* should be executed. If the function returns true, the class' Execute()
* function is called subsequently.
*/
class ASSIMP_API_WINONLY BaseProcess {
friend class Importer;
public:
/** Constructor to be privately used by Importer */
BaseProcess() AI_NO_EXCEPT;
/** Destructor, private as well */
virtual ~BaseProcess();
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with. A
* bitwise combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields,
* false if not.
*/
virtual bool IsActive( unsigned int pFlags) const = 0;
// -------------------------------------------------------------------
/** Check whether this step expects its input vertex data to be
* in verbose format. */
virtual bool RequireVerboseFormat() const;
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* The function deletes the scene if the postprocess step fails (
* the object pointer will be set to NULL).
* @param pImp Importer instance (pImp->mScene must be valid)
*/
void ExecuteOnScene( Importer* pImp);
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
virtual void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* A process should throw an ImportErrorException* if it fails.
* This method must be implemented by deriving classes.
* @param pScene The imported data to work at.
*/
virtual void Execute( aiScene* pScene) = 0;
// -------------------------------------------------------------------
/** Assign a new SharedPostProcessInfo to the step. This object
* allows multiple postprocess steps to share data.
* @param sh May be NULL
*/
inline void SetSharedData(SharedPostProcessInfo* sh) {
shared = sh;
}
// -------------------------------------------------------------------
/** Get the shared data that is assigned to the step.
*/
inline SharedPostProcessInfo* GetSharedData() {
return shared;
}
protected:
/** See the doc of #SharedPostProcessInfo for more details */
SharedPostProcessInfo* shared;
/** Currently active progress handler */
ProgressHandler* progress;
};
} // end of namespace Assimp
#endif // AI_BASEPROCESS_H_INC

View File

@ -1,155 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Bitmap.cpp
* @brief Defines bitmap format helper for textures
*
* Used for file formats which embed their textures into the model file.
*/
#include <assimp/Bitmap.h>
#include <assimp/texture.h>
#include <assimp/IOStream.hpp>
#include <assimp/ByteSwapper.h>
namespace Assimp {
void Bitmap::Save(aiTexture* texture, IOStream* file) {
if(file != NULL) {
Header header;
DIB dib;
dib.size = DIB::dib_size;
dib.width = texture->mWidth;
dib.height = texture->mHeight;
dib.planes = 1;
dib.bits_per_pixel = 8 * mBytesPerPixel;
dib.compression = 0;
dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height;
dib.x_resolution = 0;
dib.y_resolution = 0;
dib.nb_colors = 0;
dib.nb_important_colors = 0;
header.type = 0x4D42; // 'BM'
header.offset = Header::header_size + DIB::dib_size;
header.size = header.offset + dib.image_size;
header.reserved1 = 0;
header.reserved2 = 0;
WriteHeader(header, file);
WriteDIB(dib, file);
WriteData(texture, file);
}
}
template<typename T>
inline
std::size_t Copy(uint8_t* data, const T &field) {
#ifdef AI_BUILD_BIG_ENDIAN
T field_swapped=AI_BE(field);
std::memcpy(data, &field_swapped, sizeof(field)); return sizeof(field);
#else
std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field);
#endif
}
void Bitmap::WriteHeader(Header& header, IOStream* file) {
uint8_t data[Header::header_size];
std::size_t offset = 0;
offset += Copy(&data[offset], header.type);
offset += Copy(&data[offset], header.size);
offset += Copy(&data[offset], header.reserved1);
offset += Copy(&data[offset], header.reserved2);
Copy(&data[offset], header.offset);
file->Write(data, Header::header_size, 1);
}
void Bitmap::WriteDIB(DIB& dib, IOStream* file) {
uint8_t data[DIB::dib_size];
std::size_t offset = 0;
offset += Copy(&data[offset], dib.size);
offset += Copy(&data[offset], dib.width);
offset += Copy(&data[offset], dib.height);
offset += Copy(&data[offset], dib.planes);
offset += Copy(&data[offset], dib.bits_per_pixel);
offset += Copy(&data[offset], dib.compression);
offset += Copy(&data[offset], dib.image_size);
offset += Copy(&data[offset], dib.x_resolution);
offset += Copy(&data[offset], dib.y_resolution);
offset += Copy(&data[offset], dib.nb_colors);
Copy(&data[offset], dib.nb_important_colors);
file->Write(data, DIB::dib_size, 1);
}
void Bitmap::WriteData(aiTexture* texture, IOStream* file) {
static const std::size_t padding_offset = 4;
static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0};
unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset;
uint8_t pixel[mBytesPerPixel];
for(std::size_t i = 0; i < texture->mHeight; ++i) {
for(std::size_t j = 0; j < texture->mWidth; ++j) {
const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format
pixel[0] = texel.r;
pixel[1] = texel.g;
pixel[2] = texel.b;
pixel[3] = texel.a;
file->Write(pixel, mBytesPerPixel, 1);
}
file->Write(padding_data, padding, 1);
}
}
}

View File

@ -1,88 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (C) 2016 The Qt Company Ltd.
Copyright (c) 2006-2012, 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.
---------------------------------------------------------------------------
*/
#include <assimp/CreateAnimMesh.h>
namespace Assimp {
aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh)
{
aiAnimMesh *animesh = new aiAnimMesh;
animesh->mNumVertices = mesh->mNumVertices;
if (mesh->mVertices) {
animesh->mVertices = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D));
}
if (mesh->mNormals) {
animesh->mNormals = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D));
}
if (mesh->mTangents) {
animesh->mTangents = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D));
}
if (mesh->mBitangents) {
animesh->mBitangents = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D));
}
for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
if (mesh->mColors[i]) {
animesh->mColors[i] = new aiColor4D[animesh->mNumVertices];
std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D));
} else {
animesh->mColors[i] = NULL;
}
}
for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->mTextureCoords[i]) {
animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D));
} else {
animesh->mTextureCoords[i] = NULL;
}
}
return animesh;
}
} // end of namespace Assimp

View File

@ -1,154 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 DefaultIOStream.cpp
* @brief Default File I/O implementation for #Importer
*/
#include <assimp/ai_assert.h>
#include <assimp/DefaultIOStream.h>
#include <sys/types.h>
#include <sys/stat.h>
using namespace Assimp;
// ----------------------------------------------------------------------------------
DefaultIOStream::~DefaultIOStream()
{
if (mFile) {
::fclose(mFile);
mFile = nullptr;
}
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::Read(void* pvBuffer,
size_t pSize,
size_t pCount)
{
ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0);
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::Write(const void* pvBuffer,
size_t pSize,
size_t pCount)
{
ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0);
}
// ----------------------------------------------------------------------------------
aiReturn DefaultIOStream::Seek(size_t pOffset,
aiOrigin pOrigin)
{
if (!mFile) {
return AI_FAILURE;
}
// Just to check whether our enum maps one to one with the CRT constants
static_assert(aiOrigin_CUR == SEEK_CUR &&
aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, "aiOrigin_CUR == SEEK_CUR && \
aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET");
// do the seek
return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE);
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::Tell() const
{
if (!mFile) {
return 0;
}
return ::ftell(mFile);
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::FileSize() const
{
if (! mFile || mFilename.empty()) {
return 0;
}
if (SIZE_MAX == mCachedSize ) {
// Although fseek/ftell would allow us to reuse the existing file handle here,
// it is generally unsafe because:
// - For binary streams, it is not technically well-defined
// - For text files the results are meaningless
// That's why we use the safer variant fstat here.
//
// See here for details:
// https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
struct __stat64 fileStat;
//using fileno + fstat avoids having to handle the filename
int err = _fstat64( _fileno(mFile), &fileStat );
if (0 != err)
return 0;
mCachedSize = (size_t) (fileStat.st_size);
#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__
struct stat fileStat;
int err = stat(mFilename.c_str(), &fileStat );
if (0 != err)
return 0;
const unsigned long long cachedSize = fileStat.st_size;
mCachedSize = static_cast< size_t >( cachedSize );
#else
# error "Unknown platform"
#endif
}
return mCachedSize;
}
// ----------------------------------------------------------------------------------
void DefaultIOStream::Flush()
{
if (mFile) {
::fflush(mFile);
}
}
// ----------------------------------------------------------------------------------

View File

@ -1,216 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Default implementation of IOSystem using the standard C file functions */
#include <assimp/StringComparison.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/DefaultIOStream.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/ai_assert.h>
#include <stdlib.h>
#ifdef __unix__
#include <sys/param.h>
#include <stdlib.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
using namespace Assimp;
#ifdef _WIN32
static std::wstring Utf8ToWide(const char* in)
{
int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
// size includes terminating null; std::wstring adds null automatically
std::wstring out(static_cast<size_t>(size) - 1, L'\0');
MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
return out;
}
static std::string WideToUtf8(const wchar_t* in)
{
int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
// size includes terminating null; std::string adds null automatically
std::string out(static_cast<size_t>(size) - 1, '\0');
WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
return out;
}
#endif
// ------------------------------------------------------------------------------------------------
// Tests for the existence of a file at the given path.
bool DefaultIOSystem::Exists(const char* pFile) const
{
#ifdef _WIN32
struct __stat64 filestat;
if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
return false;
}
#else
FILE* file = ::fopen(pFile, "rb");
if (!file)
return false;
::fclose(file);
#endif
return true;
}
// ------------------------------------------------------------------------------------------------
// Open a new file with a given path.
IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
{
ai_assert(strFile != nullptr);
ai_assert(strMode != nullptr);
FILE* file;
#ifdef _WIN32
file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
#else
file = ::fopen(strFile, strMode);
#endif
if (!file)
return nullptr;
return new DefaultIOStream(file, strFile);
}
// ------------------------------------------------------------------------------------------------
// Closes the given file and releases all resources associated with it.
void DefaultIOSystem::Close(IOStream* pFile)
{
delete pFile;
}
// ------------------------------------------------------------------------------------------------
// Returns the operation specific directory separator
char DefaultIOSystem::getOsSeparator() const
{
#ifndef _WIN32
return '/';
#else
return '\\';
#endif
}
// ------------------------------------------------------------------------------------------------
// IOSystem default implementation (ComparePaths isn't a pure virtual function)
bool IOSystem::ComparePaths(const char* one, const char* second) const
{
return !ASSIMP_stricmp(one, second);
}
// ------------------------------------------------------------------------------------------------
// Convert a relative path into an absolute path
inline static std::string MakeAbsolutePath(const char* in)
{
ai_assert(in);
std::string out;
#ifdef _WIN32
wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
if (ret) {
out = WideToUtf8(ret);
free(ret);
}
#else
char* ret = realpath(in, nullptr);
if (ret) {
out = ret;
free(ret);
}
#endif
if (!ret) {
// preserve the input path, maybe someone else is able to fix
// the path before it is accessed (e.g. our file system filter)
ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
out = in;
}
return out;
}
// ------------------------------------------------------------------------------------------------
// DefaultIOSystem's more specialized implementation
bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
{
// chances are quite good both paths are formatted identically,
// so we can hopefully return here already
if (!ASSIMP_stricmp(one, second))
return true;
std::string temp1 = MakeAbsolutePath(one);
std::string temp2 = MakeAbsolutePath(second);
return !ASSIMP_stricmp(temp1, temp2);
}
// ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::fileName(const std::string& path)
{
std::string ret = path;
std::size_t last = ret.find_last_of("\\/");
if (last != std::string::npos) ret = ret.substr(last + 1);
return ret;
}
// ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::completeBaseName(const std::string& path)
{
std::string ret = fileName(path);
std::size_t pos = ret.find_last_of('.');
if (pos != std::string::npos) ret = ret.substr(0, pos);
return ret;
}
// ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::absolutePath(const std::string& path)
{
std::string ret = path;
std::size_t last = ret.find_last_of("\\/");
if (last != std::string::npos) ret = ret.substr(0, last);
return ret;
}
// ------------------------------------------------------------------------------------------------

View File

@ -1,418 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 DefaultLogger.cpp
* @brief Implementation of DefaultLogger (and Logger)
*/
// Default log streams
#include "Win32DebugLogStream.h"
#include "StdOStreamLogStream.h"
#include "FileLogStream.h"
#include <assimp/StringUtils.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/NullLogger.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/ai_assert.h>
#include <iostream>
#include <stdio.h>
#ifndef ASSIMP_BUILD_SINGLETHREADED
# include <thread>
# include <mutex>
std::mutex loggerMutex;
#endif
namespace Assimp {
// ----------------------------------------------------------------------------------
NullLogger DefaultLogger::s_pNullLogger;
Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger;
static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
// ----------------------------------------------------------------------------------
// Represents a log-stream + its error severity
struct LogStreamInfo {
unsigned int m_uiErrorSeverity;
LogStream *m_pStream;
// Constructor
LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) :
m_uiErrorSeverity( uiErrorSev ),
m_pStream( pStream ) {
// empty
}
// Destructor
~LogStreamInfo() {
delete m_pStream;
}
};
// ----------------------------------------------------------------------------------
// Construct a default log stream
LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
const char* name /*= "AssimpLog.txt"*/,
IOSystem* io /*= NULL*/)
{
switch (streams)
{
// This is a platform-specific feature
case aiDefaultLogStream_DEBUGGER:
#ifdef WIN32
return new Win32DebugLogStream();
#else
return nullptr;
#endif
// Platform-independent default streams
case aiDefaultLogStream_STDERR:
return new StdOStreamLogStream(std::cerr);
case aiDefaultLogStream_STDOUT:
return new StdOStreamLogStream(std::cout);
case aiDefaultLogStream_FILE:
return (name && *name ? new FileLogStream(name,io) : nullptr );
default:
// We don't know this default log stream, so raise an assertion
ai_assert(false);
};
// For compilers without dead code path detection
return NULL;
}
// ----------------------------------------------------------------------------------
// Creates the only singleton instance
Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
LogSeverity severity /*= NORMAL*/,
unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
IOSystem* io /*= NULL*/) {
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(loggerMutex);
#endif
if ( m_pLogger && !isNullLogger() ) {
delete m_pLogger;
}
m_pLogger = new DefaultLogger( severity );
// Attach default log streams
// Stream the log to the MSVC debugger?
if ( defStreams & aiDefaultLogStream_DEBUGGER ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) );
}
// Stream the log to COUT?
if ( defStreams & aiDefaultLogStream_STDOUT ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) );
}
// Stream the log to CERR?
if ( defStreams & aiDefaultLogStream_STDERR ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) );
}
// Stream the log to a file
if ( defStreams & aiDefaultLogStream_FILE && name && *name ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) );
}
return m_pLogger;
}
// ----------------------------------------------------------------------------------
void Logger::debug(const char* message) {
// SECURITY FIX: otherwise it's easy to produce overruns since
// sometimes importers will include data from the input file
// (i.e. node names) in their messages.
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
}
return OnDebug(message);
}
// ----------------------------------------------------------------------------------
void Logger::info(const char* message) {
// SECURITY FIX: see above
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
}
return OnInfo(message);
}
// ----------------------------------------------------------------------------------
void Logger::warn(const char* message) {
// SECURITY FIX: see above
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
}
return OnWarn(message);
}
// ----------------------------------------------------------------------------------
void Logger::error(const char* message) {
// SECURITY FIX: see above
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
}
return OnError(message);
}
// ----------------------------------------------------------------------------------
void DefaultLogger::set( Logger *logger ) {
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(loggerMutex);
#endif
if ( nullptr == logger ) {
logger = &s_pNullLogger;
}
if ( nullptr != m_pLogger && !isNullLogger() ) {
delete m_pLogger;
}
DefaultLogger::m_pLogger = logger;
}
// ----------------------------------------------------------------------------------
bool DefaultLogger::isNullLogger() {
return m_pLogger == &s_pNullLogger;
}
// ----------------------------------------------------------------------------------
Logger *DefaultLogger::get() {
return m_pLogger;
}
// ----------------------------------------------------------------------------------
// Kills the only instance
void DefaultLogger::kill() {
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(loggerMutex);
#endif
if ( m_pLogger == &s_pNullLogger ) {
return;
}
delete m_pLogger;
m_pLogger = &s_pNullLogger;
}
// ----------------------------------------------------------------------------------
// Debug message
void DefaultLogger::OnDebug( const char* message ) {
if ( m_Severity == Logger::NORMAL ) {
return;
}
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message);
WriteToStreams( msg, Logger::Debugging );
}
// ----------------------------------------------------------------------------------
// Logs an info
void DefaultLogger::OnInfo( const char* message ){
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message );
WriteToStreams( msg , Logger::Info );
}
// ----------------------------------------------------------------------------------
// Logs a warning
void DefaultLogger::OnWarn( const char* message ) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message );
WriteToStreams( msg, Logger::Warn );
}
// ----------------------------------------------------------------------------------
// Logs an error
void DefaultLogger::OnError( const char* message ) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[ Size ];
ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message );
WriteToStreams( msg, Logger::Err );
}
// ----------------------------------------------------------------------------------
// Will attach a new stream
bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) {
if ( nullptr == pStream ) {
return false;
}
if (0 == severity) {
severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
}
for ( StreamIt it = m_StreamArray.begin();
it != m_StreamArray.end();
++it )
{
if ( (*it)->m_pStream == pStream ) {
(*it)->m_uiErrorSeverity |= severity;
return true;
}
}
LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
m_StreamArray.push_back( pInfo );
return true;
}
// ----------------------------------------------------------------------------------
// Detach a stream
bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) {
if ( nullptr == pStream ) {
return false;
}
if (0 == severity) {
severity = SeverityAll;
}
bool res( false );
for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
if ( (*it)->m_pStream == pStream ) {
(*it)->m_uiErrorSeverity &= ~severity;
if ( (*it)->m_uiErrorSeverity == 0 ) {
// don't delete the underlying stream 'cause the caller gains ownership again
(**it).m_pStream = nullptr;
delete *it;
m_StreamArray.erase( it );
res = true;
break;
}
return true;
}
}
return res;
}
// ----------------------------------------------------------------------------------
// Constructor
DefaultLogger::DefaultLogger(LogSeverity severity)
: Logger ( severity )
, noRepeatMsg (false)
, lastLen( 0 ) {
lastMsg[0] = '\0';
}
// ----------------------------------------------------------------------------------
// Destructor
DefaultLogger::~DefaultLogger() {
for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
// also frees the underlying stream, we are its owner.
delete *it;
}
}
// ----------------------------------------------------------------------------------
// Writes message to stream
void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) {
ai_assert(nullptr != message);
// Check whether this is a repeated message
if (! ::strncmp( message,lastMsg, lastLen-1))
{
if (!noRepeatMsg)
{
noRepeatMsg = true;
message = "Skipping one or more lines with the same contents\n";
}
else return;
}
else
{
// append a new-line character to the message to be printed
lastLen = ::strlen(message);
::memcpy(lastMsg,message,lastLen+1);
::strcat(lastMsg+lastLen,"\n");
message = lastMsg;
noRepeatMsg = false;
++lastLen;
}
for ( ConstStreamIt it = m_StreamArray.begin();
it != m_StreamArray.end();
++it)
{
if ( ErrorSev & (*it)->m_uiErrorSeverity )
(*it)->m_pStream->write( message);
}
}
// ----------------------------------------------------------------------------------
// Returns thread id, if not supported only a zero will be returned.
unsigned int DefaultLogger::GetThreadID()
{
// fixme: we can get this value via std::threads
// std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case.
#ifdef WIN32
return (unsigned int)::GetCurrentThreadId();
#else
return 0; // not supported
#endif
}
// ----------------------------------------------------------------------------------
} // !namespace Assimp

View File

@ -1,65 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 ProgressHandler.hpp
* @brief Abstract base class 'ProgressHandler'.
*/
#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
#include <assimp/ProgressHandler.hpp>
namespace Assimp {
// ------------------------------------------------------------------------------------
/** @brief Internal default implementation of the #ProgressHandler interface. */
class DefaultProgressHandler : public ProgressHandler {
virtual bool Update(float /*percentage*/) {
return false;
}
}; // !class DefaultProgressHandler
} // Namespace Assimp
#endif

View File

@ -1,636 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Exporter.cpp
Assimp export interface. While it's public interface bears many similarities
to the import interface (in fact, it is largely symmetric), the internal
implementations differs a lot. Exporters are considered stateless and are
simple callbacks which we maintain in a global list along with their
description strings.
Here we implement only the C++ interface (Assimp::Exporter).
*/
#ifndef ASSIMP_BUILD_NO_EXPORT
#include <assimp/BlobIOSystem.h>
#include <assimp/SceneCombiner.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/Exporter.hpp>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/Exceptional.h>
#include "Common/DefaultProgressHandler.h"
#include "Common/BaseProcess.h"
#include "Common/ScenePrivate.h"
#include "PostProcessing/CalcTangentsProcess.h"
#include "PostProcessing/MakeVerboseFormat.h"
#include "PostProcessing/JoinVerticesProcess.h"
#include "PostProcessing/ConvertToLHProcess.h"
#include "PostProcessing/PretransformVertices.h"
#include <memory>
namespace Assimp {
// PostStepRegistry.cpp
void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
// ------------------------------------------------------------------------------------------------
// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype
// do not use const, because some exporter need to convert the scene temporary
void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneA3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
// ------------------------------------------------------------------------------------------------
// global array of all export formats which Assimp supports in its current build
Exporter::ExportFormatEntry gExporters[] =
{
#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ),
#endif
#ifndef ASSIMP_BUILD_NO_X_EXPORTER
Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ),
#endif
#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
#endif
#ifndef ASSIMP_BUILD_NO_STL_EXPORTER
Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
),
Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
),
#endif
#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly,
aiProcess_PreTransformVertices
),
Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
aiProcess_PreTransformVertices
),
#endif
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS,
aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ),
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
Exporter::ExportFormatEntry( "assbin", "Assimp Binary File", "assbin" , &ExportSceneAssbin, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
Exporter::ExportFormatEntry( "assxml", "Assimp XML Document", "assxml" , &ExportSceneAssxml, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ),
Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
Exporter::ExportFormatEntry( "m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0 ),
Exporter::ExportFormatEntry( "a3d", "Model 3D (ascii)", "m3d", &ExportSceneA3D, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
Exporter::ExportFormatEntry( "assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0)
#endif
};
#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
class ExporterPimpl {
public:
ExporterPimpl()
: blob()
, mIOSystem(new Assimp::DefaultIOSystem())
, mIsDefaultIOHandler(true)
, mProgressHandler( nullptr )
, mIsDefaultProgressHandler( true )
, mPostProcessingSteps()
, mError()
, mExporters() {
GetPostProcessingStepInstanceList(mPostProcessingSteps);
// grab all built-in exporters
if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) {
mExporters.resize( ASSIMP_NUM_EXPORTERS );
std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() );
}
}
~ExporterPimpl() {
delete blob;
// Delete all post-processing plug-ins
for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) {
delete mPostProcessingSteps[a];
}
delete mProgressHandler;
}
public:
aiExportDataBlob* blob;
std::shared_ptr< Assimp::IOSystem > mIOSystem;
bool mIsDefaultIOHandler;
/** The progress handler */
ProgressHandler *mProgressHandler;
bool mIsDefaultProgressHandler;
/** Post processing steps we can apply at the imported data. */
std::vector< BaseProcess* > mPostProcessingSteps;
/** Last fatal export error */
std::string mError;
/** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */
std::vector<Exporter::ExportFormatEntry> mExporters;
};
} // end of namespace Assimp
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
Exporter :: Exporter()
: pimpl(new ExporterPimpl()) {
pimpl->mProgressHandler = new DefaultProgressHandler();
}
// ------------------------------------------------------------------------------------------------
Exporter::~Exporter() {
FreeBlob();
delete pimpl;
}
// ------------------------------------------------------------------------------------------------
void Exporter::SetIOHandler( IOSystem* pIOHandler) {
pimpl->mIsDefaultIOHandler = !pIOHandler;
pimpl->mIOSystem.reset(pIOHandler);
}
// ------------------------------------------------------------------------------------------------
IOSystem* Exporter::GetIOHandler() const {
return pimpl->mIOSystem.get();
}
// ------------------------------------------------------------------------------------------------
bool Exporter::IsDefaultIOHandler() const {
return pimpl->mIsDefaultIOHandler;
}
// ------------------------------------------------------------------------------------------------
void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
ai_assert(nullptr != pimpl);
if ( nullptr == pHandler) {
// Release pointer in the possession of the caller
pimpl->mProgressHandler = new DefaultProgressHandler();
pimpl->mIsDefaultProgressHandler = true;
return;
}
if (pimpl->mProgressHandler == pHandler) {
return;
}
delete pimpl->mProgressHandler;
pimpl->mProgressHandler = pHandler;
pimpl->mIsDefaultProgressHandler = false;
}
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
unsigned int pPreprocessing, const ExportProperties* pProperties) {
if (pimpl->blob) {
delete pimpl->blob;
pimpl->blob = nullptr;
}
std::shared_ptr<IOSystem> old = pimpl->mIOSystem;
BlobIOSystem* blobio = new BlobIOSystem();
pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
pimpl->mIOSystem = old;
return nullptr;
}
pimpl->blob = blobio->GetBlobChain();
pimpl->mIOSystem = old;
return pimpl->blob;
}
// ------------------------------------------------------------------------------------------------
aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
unsigned int pPreprocessing, const ExportProperties* pProperties) {
ASSIMP_BEGIN_EXCEPTION_REGION();
// when they create scenes from scratch, users will likely create them not in verbose
// format. They will likely not be aware that there is a flag in the scene to indicate
// this, however. To avoid surprises and bug reports, we check for duplicates in
// meshes upfront.
const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene);
pimpl->mProgressHandler->UpdateFileWrite(0, 4);
pimpl->mError = "";
for (size_t i = 0; i < pimpl->mExporters.size(); ++i) {
const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i];
if (!strcmp(exp.mDescription.id,pFormatId)) {
try {
// Always create a full copy of the scene. We might optimize this one day,
// but for now it is the most pragmatic way.
aiScene* scenecopy_tmp = nullptr;
SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
pimpl->mProgressHandler->UpdateFileWrite(1, 4);
std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
const ScenePrivateData* const priv = ScenePriv(pScene);
// steps that are not idempotent, i.e. we might need to run them again, usually to get back to the
// original state before the step was applied first. When checking which steps we don't need
// to run, those are excluded.
const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded;
// Erase all pp steps that were already applied to this scene
const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy
? (priv->mPPStepsApplied & ~nonIdempotentSteps)
: 0u);
// If no extra post-processing was specified, and we obtained this scene from an
// Assimp importer, apply the reverse steps automatically.
// TODO: either drop this, or document it. Otherwise it is just a bad surprise.
//if (!pPreprocessing && priv) {
// pp |= (nonIdempotentSteps & priv->mPPStepsApplied);
//}
// If the input scene is not in verbose format, but there is at least post-processing step that relies on it,
// we need to run the MakeVerboseFormat step first.
bool must_join_again = false;
if (!is_verbose_format) {
bool verbosify = false;
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
BaseProcess* const p = pimpl->mPostProcessingSteps[a];
if (p->IsActive(pp) && p->RequireVerboseFormat()) {
verbosify = true;
break;
}
}
if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first");
MakeVerboseFormatProcess proc;
proc.Execute(scenecopy.get());
if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
must_join_again = true;
}
}
}
pimpl->mProgressHandler->UpdateFileWrite(2, 4);
if (pp) {
// the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout
{
FlipWindingOrderProcess step;
if (step.IsActive(pp)) {
step.Execute(scenecopy.get());
}
}
{
FlipUVsProcess step;
if (step.IsActive(pp)) {
step.Execute(scenecopy.get());
}
}
{
MakeLeftHandedProcess step;
if (step.IsActive(pp)) {
step.Execute(scenecopy.get());
}
}
bool exportPointCloud(false);
if (nullptr != pProperties) {
exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
}
// dispatch other processes
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
BaseProcess* const p = pimpl->mPostProcessingSteps[a];
if (p->IsActive(pp)
&& !dynamic_cast<FlipUVsProcess*>(p)
&& !dynamic_cast<FlipWindingOrderProcess*>(p)
&& !dynamic_cast<MakeLeftHandedProcess*>(p)) {
if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) {
continue;
}
p->Execute(scenecopy.get());
}
}
ScenePrivateData* const privOut = ScenePriv(scenecopy.get());
ai_assert(nullptr != privOut);
privOut->mPPStepsApplied |= pp;
}
pimpl->mProgressHandler->UpdateFileWrite(3, 4);
if(must_join_again) {
JoinVerticesProcess proc;
proc.Execute(scenecopy.get());
}
ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry.
ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
pimpl->mProgressHandler->UpdateFileWrite(4, 4);
} catch (DeadlyExportError& err) {
pimpl->mError = err.what();
return AI_FAILURE;
}
return AI_SUCCESS;
}
}
pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId;
ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE;
}
// ------------------------------------------------------------------------------------------------
const char* Exporter::GetErrorString() const {
return pimpl->mError.c_str();
}
// ------------------------------------------------------------------------------------------------
void Exporter::FreeBlob() {
delete pimpl->blob;
pimpl->blob = nullptr;
pimpl->mError = "";
}
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetBlob() const {
return pimpl->blob;
}
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
const aiExportDataBlob* tmp = pimpl->blob;
pimpl->blob = nullptr;
return tmp;
}
// ------------------------------------------------------------------------------------------------
size_t Exporter::GetExportFormatCount() const {
return pimpl->mExporters.size();
}
// ------------------------------------------------------------------------------------------------
const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
if (index >= GetExportFormatCount()) {
return nullptr;
}
// Return from static storage if the requested index is built-in.
if (index < sizeof(gExporters) / sizeof(gExporters[0])) {
return &gExporters[index].mDescription;
}
return &pimpl->mExporters[index].mDescription;
}
// ------------------------------------------------------------------------------------------------
aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
for(const ExportFormatEntry& e : pimpl->mExporters) {
if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
return aiReturn_FAILURE;
}
}
pimpl->mExporters.push_back(desc);
return aiReturn_SUCCESS;
}
// ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) {
for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
it != pimpl->mExporters.end(); ++it) {
if (!strcmp((*it).mDescription.id,id)) {
pimpl->mExporters.erase(it);
break;
}
}
}
// ------------------------------------------------------------------------------------------------
ExportProperties::ExportProperties() {
// empty
}
// ------------------------------------------------------------------------------------------------
ExportProperties::ExportProperties(const ExportProperties &other)
: mIntProperties(other.mIntProperties)
, mFloatProperties(other.mFloatProperties)
, mStringProperties(other.mStringProperties)
, mMatrixProperties(other.mMatrixProperties) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Set a configuration property
bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) {
return SetGenericProperty<int>(mIntProperties, szName,iValue);
}
// ------------------------------------------------------------------------------------------------
// Set a configuration property
bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) {
return SetGenericProperty<ai_real>(mFloatProperties, szName,iValue);
}
// ------------------------------------------------------------------------------------------------
// Set a configuration property
bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) {
return SetGenericProperty<std::string>(mStringProperties, szName,value);
}
// ------------------------------------------------------------------------------------------------
// Set a configuration property
bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
return SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
return GetGenericProperty<ai_real>(mFloatProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
const std::string ExportProperties::GetPropertyString(const char* szName,
const std::string& iErrorReturn /*= ""*/) const {
return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Has a configuration property
const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName,
const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Has a configuration property
bool ExportProperties::HasPropertyInteger(const char* szName) const {
return HasGenericProperty<int>(mIntProperties, szName);
}
// ------------------------------------------------------------------------------------------------
// Has a configuration property
bool ExportProperties::HasPropertyBool(const char* szName) const {
return HasGenericProperty<int>(mIntProperties, szName);
}
// ------------------------------------------------------------------------------------------------
// Has a configuration property
bool ExportProperties::HasPropertyFloat(const char* szName) const {
return HasGenericProperty<ai_real>(mFloatProperties, szName);
}
// ------------------------------------------------------------------------------------------------
// Has a configuration property
bool ExportProperties::HasPropertyString(const char* szName) const {
return HasGenericProperty<std::string>(mStringProperties, szName);
}
// ------------------------------------------------------------------------------------------------
// Has a configuration property
bool ExportProperties::HasPropertyMatrix(const char* szName) const {
return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName);
}
#endif // !ASSIMP_BUILD_NO_EXPORT

View File

@ -1,107 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 FileLofStream.h
*/
#ifndef ASSIMP_FILELOGSTREAM_H_INC
#define ASSIMP_FILELOGSTREAM_H_INC
#include <assimp/LogStream.hpp>
#include <assimp/IOStream.hpp>
#include <assimp/DefaultIOSystem.h>
namespace Assimp {
// ----------------------------------------------------------------------------------
/** @class FileLogStream
* @brief Logstream to write into a file.
*/
class FileLogStream :
public LogStream
{
public:
FileLogStream( const char* file, IOSystem* io = NULL );
~FileLogStream();
void write( const char* message );
private:
IOStream *m_pStream;
};
// ----------------------------------------------------------------------------------
// Constructor
inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) :
m_pStream(NULL)
{
if ( !file || 0 == *file )
return;
// If no IOSystem is specified: take a default one
if (!io)
{
DefaultIOSystem FileSystem;
m_pStream = FileSystem.Open( file, "wt");
}
else m_pStream = io->Open( file, "wt" );
}
// ----------------------------------------------------------------------------------
// Destructor
inline FileLogStream::~FileLogStream()
{
// The virtual d'tor should destroy the underlying file
delete m_pStream;
}
// ----------------------------------------------------------------------------------
// Write method
inline void FileLogStream::write( const char* message )
{
if (m_pStream != NULL)
{
m_pStream->Write(message, sizeof(char), ::strlen(message));
m_pStream->Flush();
}
}
// ----------------------------------------------------------------------------------
} // !Namespace Assimp
#endif // !! ASSIMP_FILELOGSTREAM_H_INC

View File

@ -1,345 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2008, 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 FileSystemFilter.h
* Implements a filter system to filter calls to Exists() and Open()
* in order to improve the success rate of file opening ...
*/
#pragma once
#ifndef AI_FILESYSTEMFILTER_H_INC
#define AI_FILESYSTEMFILTER_H_INC
#include <assimp/IOSystem.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/fast_atof.h>
#include <assimp/ParsingUtils.h>
namespace Assimp {
inline bool IsHex(char s) {
return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F');
}
// ---------------------------------------------------------------------------
/** File system filter
*/
class FileSystemFilter : public IOSystem
{
public:
/** Constructor. */
FileSystemFilter(const std::string& file, IOSystem* old)
: mWrapped (old)
, mSrc_file(file)
, mSep(mWrapped->getOsSeparator()) {
ai_assert(nullptr != mWrapped);
// Determine base directory
mBase = mSrc_file;
std::string::size_type ss2;
if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) {
mBase.erase(ss2,mBase.length()-ss2);
} else {
mBase = "";
}
// make sure the directory is terminated properly
char s;
if ( mBase.empty() ) {
mBase = ".";
mBase += getOsSeparator();
} else if ((s = *(mBase.end()-1)) != '\\' && s != '/') {
mBase += getOsSeparator();
}
DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'");
}
/** Destructor. */
~FileSystemFilter() {
// empty
}
// -------------------------------------------------------------------
/** Tests for the existence of a file at the given path. */
bool Exists( const char* pFile) const {
ai_assert( nullptr != mWrapped );
std::string tmp = pFile;
// Currently this IOSystem is also used to open THE ONE FILE.
if (tmp != mSrc_file) {
BuildPath(tmp);
Cleanup(tmp);
}
return mWrapped->Exists(tmp);
}
// -------------------------------------------------------------------
/** Returns the directory separator. */
char getOsSeparator() const {
return mSep;
}
// -------------------------------------------------------------------
/** Open a new file with a given path. */
IOStream* Open( const char* pFile, const char* pMode = "rb") {
ai_assert( nullptr != mWrapped );
if ( nullptr == pFile || nullptr == pMode ) {
return nullptr;
}
ai_assert( nullptr != pFile );
ai_assert( nullptr != pMode );
// First try the unchanged path
IOStream* s = mWrapped->Open(pFile,pMode);
if (nullptr == s) {
std::string tmp = pFile;
// Try to convert between absolute and relative paths
BuildPath(tmp);
s = mWrapped->Open(tmp,pMode);
if (nullptr == s) {
// Finally, look for typical issues with paths
// and try to correct them. This is our last
// resort.
tmp = pFile;
Cleanup(tmp);
BuildPath(tmp);
s = mWrapped->Open(tmp,pMode);
}
}
return s;
}
// -------------------------------------------------------------------
/** Closes the given file and releases all resources associated with it. */
void Close( IOStream* pFile) {
ai_assert( nullptr != mWrapped );
return mWrapped->Close(pFile);
}
// -------------------------------------------------------------------
/** Compare two paths */
bool ComparePaths (const char* one, const char* second) const {
ai_assert( nullptr != mWrapped );
return mWrapped->ComparePaths (one,second);
}
// -------------------------------------------------------------------
/** Pushes a new directory onto the directory stack. */
bool PushDirectory(const std::string &path ) {
ai_assert( nullptr != mWrapped );
return mWrapped->PushDirectory(path);
}
// -------------------------------------------------------------------
/** Returns the top directory from the stack. */
const std::string &CurrentDirectory() const {
ai_assert( nullptr != mWrapped );
return mWrapped->CurrentDirectory();
}
// -------------------------------------------------------------------
/** Returns the number of directories stored on the stack. */
size_t StackSize() const {
ai_assert( nullptr != mWrapped );
return mWrapped->StackSize();
}
// -------------------------------------------------------------------
/** Pops the top directory from the stack. */
bool PopDirectory() {
ai_assert( nullptr != mWrapped );
return mWrapped->PopDirectory();
}
// -------------------------------------------------------------------
/** Creates an new directory at the given path. */
bool CreateDirectory(const std::string &path) {
ai_assert( nullptr != mWrapped );
return mWrapped->CreateDirectory(path);
}
// -------------------------------------------------------------------
/** Will change the current directory to the given path. */
bool ChangeDirectory(const std::string &path) {
ai_assert( nullptr != mWrapped );
return mWrapped->ChangeDirectory(path);
}
// -------------------------------------------------------------------
/** Delete file. */
bool DeleteFile(const std::string &file) {
ai_assert( nullptr != mWrapped );
return mWrapped->DeleteFile(file);
}
private:
// -------------------------------------------------------------------
/** Build a valid path from a given relative or absolute path.
*/
void BuildPath (std::string& in) const {
ai_assert( nullptr != mWrapped );
// if we can already access the file, great.
if (in.length() < 3 || mWrapped->Exists(in)) {
return;
}
// Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
if (in[1] != ':') {
// append base path and try
const std::string tmp = mBase + in;
if (mWrapped->Exists(tmp)) {
in = tmp;
return;
}
}
// Chop of the file name and look in the model directory, if
// this fails try all sub paths of the given path, i.e.
// if the given path is foo/bar/something.lwo, try
// <base>/something.lwo
// <base>/bar/something.lwo
// <base>/foo/bar/something.lwo
std::string::size_type pos = in.rfind('/');
if (std::string::npos == pos) {
pos = in.rfind('\\');
}
if (std::string::npos != pos) {
std::string tmp;
std::string::size_type last_dirsep = std::string::npos;
while(true) {
tmp = mBase;
tmp += mSep;
std::string::size_type dirsep = in.rfind('/', last_dirsep);
if (std::string::npos == dirsep) {
dirsep = in.rfind('\\', last_dirsep);
}
if (std::string::npos == dirsep || dirsep == 0) {
// we did try this already.
break;
}
last_dirsep = dirsep-1;
tmp += in.substr(dirsep+1, in.length()-pos);
if (mWrapped->Exists(tmp)) {
in = tmp;
return;
}
}
}
// hopefully the underlying file system has another few tricks to access this file ...
}
// -------------------------------------------------------------------
/** Cleanup the given path
*/
void Cleanup (std::string& in) const {
if(in.empty()) {
return;
}
// Remove a very common issue when we're parsing file names: spaces at the
// beginning of the path.
char last = 0;
std::string::iterator it = in.begin();
while (IsSpaceOrNewLine( *it ))++it;
if (it != in.begin()) {
in.erase(in.begin(),it+1);
}
const char separator = getOsSeparator();
for (it = in.begin(); it != in.end(); ++it) {
// Exclude :// and \\, which remain untouched.
// https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
if ( !strncmp(&*it, "://", 3 )) {
it += 3;
continue;
}
if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
it += 2;
continue;
}
// Cleanup path delimiters
if (*it == '/' || (*it) == '\\') {
*it = separator;
// And we're removing double delimiters, frequent issue with
// incorrectly composited paths ...
if (last == *it) {
it = in.erase(it);
--it;
}
} else if (*it == '%' && in.end() - it > 2) {
// Hex sequence in URIs
if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) {
*it = HexOctetToDecimal(&*it);
it = in.erase(it+1,it+2);
--it;
}
}
last = *it;
}
}
private:
IOSystem *mWrapped;
std::string mSrc_file, mBase;
char mSep;
};
} //!ns Assimp
#endif //AI_DEFAULTIOSYSTEM_H_INC

View File

@ -1,102 +0,0 @@
// Definitions for the Interchange File Format (IFF)
// Alexander Gessler, 2006
// Adapted to Assimp August 2008
#ifndef AI_IFF_H_INCLUDED
#define AI_IFF_H_INCLUDED
#include <assimp/ByteSwapper.h>
namespace Assimp {
namespace IFF {
/////////////////////////////////////////////////////////////////////////////////
//! Describes an IFF chunk header
/////////////////////////////////////////////////////////////////////////////////
struct ChunkHeader
{
//! Type of the chunk header - FourCC
uint32_t type;
//! Length of the chunk data, in bytes
uint32_t length;
};
/////////////////////////////////////////////////////////////////////////////////
//! Describes an IFF sub chunk header
/////////////////////////////////////////////////////////////////////////////////
struct SubChunkHeader
{
//! Type of the chunk header - FourCC
uint32_t type;
//! Length of the chunk data, in bytes
uint16_t length;
};
#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M')
/////////////////////////////////////////////////////////////////////////////////
//! Load a chunk header
//! @param outFile Pointer to the file data - points to the chunk data afterwards
//! @return Copy of the chunk header
/////////////////////////////////////////////////////////////////////////////////
inline ChunkHeader LoadChunk(uint8_t*& outFile)
{
ChunkHeader head;
::memcpy(&head.type, outFile, 4);
outFile += 4;
::memcpy(&head.length, outFile, 4);
outFile += 4;
AI_LSWAP4(head.length);
AI_LSWAP4(head.type);
return head;
}
/////////////////////////////////////////////////////////////////////////////////
//! Load a sub chunk header
//! @param outFile Pointer to the file data - points to the chunk data afterwards
//! @return Copy of the sub chunk header
/////////////////////////////////////////////////////////////////////////////////
inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
{
SubChunkHeader head;
::memcpy(&head.type, outFile, 4);
outFile += 4;
::memcpy(&head.length, outFile, 2);
outFile += 2;
AI_LSWAP2(head.length);
AI_LSWAP4(head.type);
return head;
}
/////////////////////////////////////////////////////////////////////////////////
//! Read the file header and return the type of the file and its size
//! @param outFile Pointer to the file data. The buffer must at
//! least be 12 bytes large.
//! @param fileType Receives the type of the file
//! @return 0 if everything was OK, otherwise an error message
/////////////////////////////////////////////////////////////////////////////////
inline const char* ReadHeader(uint8_t* outFile, uint32_t& fileType)
{
ChunkHeader head = LoadChunk(outFile);
if(AI_IFF_FOURCC_FORM != head.type)
{
return "The file is not an IFF file: FORM chunk is missing";
}
::memcpy(&fileType, outFile, 4);
AI_LSWAP4(fileType);
return 0;
}
}}
#endif // !! AI_IFF_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -1,247 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Importer.h mostly internal stuff for use by #Assimp::Importer */
#pragma once
#ifndef INCLUDED_AI_IMPORTER_H
#define INCLUDED_AI_IMPORTER_H
#include <map>
#include <vector>
#include <string>
#include <assimp/matrix4x4.h>
struct aiScene;
namespace Assimp {
class ProgressHandler;
class IOSystem;
class BaseImporter;
class BaseProcess;
class SharedPostProcessInfo;
//! @cond never
// ---------------------------------------------------------------------------
/** @brief Internal PIMPL implementation for Assimp::Importer
*
* Using this idiom here allows us to drop the dependency from
* std::vector and std::map in the public headers. Furthermore we are dropping
* any STL interface problems caused by mismatching STL settings. All
* size calculation are now done by us, not the app heap. */
class ImporterPimpl {
public:
// Data type to store the key hash
typedef unsigned int KeyType;
// typedefs for our four 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, ai_real> FloatPropertyMap;
typedef std::map<KeyType, std::string> StringPropertyMap;
typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
/** IO handler to use for all file accesses. */
IOSystem* mIOHandler;
bool mIsDefaultHandler;
/** Progress handler for feedback. */
ProgressHandler* mProgressHandler;
bool mIsDefaultProgressHandler;
/** Format-specific importer worker objects - one for each format we can read.*/
std::vector< BaseImporter* > mImporter;
/** Post processing steps we can apply at the imported data. */
std::vector< BaseProcess* > mPostProcessingSteps;
/** The imported data, if ReadFile() was successful, NULL otherwise. */
aiScene* mScene;
/** The error description, if there was one. */
std::string mErrorString;
/** List of integer properties */
IntPropertyMap mIntProperties;
/** List of floating-point properties */
FloatPropertyMap mFloatProperties;
/** List of string properties */
StringPropertyMap mStringProperties;
/** List of Matrix properties */
MatrixPropertyMap mMatrixProperties;
/** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
* to be executed before and after every single post-process step */
bool bExtraVerbose;
/** Used by post-process steps to share data */
SharedPostProcessInfo* mPPShared;
/// The default class constructor.
ImporterPimpl() AI_NO_EXCEPT;
};
inline
ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT
: mIOHandler( nullptr )
, mIsDefaultHandler( false )
, mProgressHandler( nullptr )
, mIsDefaultProgressHandler( false )
, mImporter()
, mPostProcessingSteps()
, mScene( nullptr )
, mErrorString()
, mIntProperties()
, mFloatProperties()
, mStringProperties()
, mMatrixProperties()
, bExtraVerbose( false )
, mPPShared( nullptr ) {
// empty
}
//! @endcond
struct BatchData;
// ---------------------------------------------------------------------------
/** FOR IMPORTER PLUGINS ONLY: A helper class to the pleasure of importers
* that need to load many external meshes recursively.
*
* The class uses several threads to load these meshes (or at least it
* could, this has not yet been implemented at the moment).
*
* @note The class may not be used by more than one thread*/
class ASSIMP_API BatchLoader
{
// friend of Importer
public:
//! @cond never
// -------------------------------------------------------------------
/** Wraps a full list of configuration properties for an importer.
* Properties can be set using SetGenericProperty */
struct PropertyMap
{
ImporterPimpl::IntPropertyMap ints;
ImporterPimpl::FloatPropertyMap floats;
ImporterPimpl::StringPropertyMap strings;
ImporterPimpl::MatrixPropertyMap matrices;
bool operator == (const PropertyMap& prop) const {
// fixme: really isocpp? gcc complains
return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices;
}
bool empty () const {
return ints.empty() && floats.empty() && strings.empty() && matrices.empty();
}
};
//! @endcond
public:
// -------------------------------------------------------------------
/** Construct a batch loader from a given IO system to be used
* to access external files
*/
explicit BatchLoader(IOSystem* pIO, bool validate = false );
// -------------------------------------------------------------------
/** The class destructor.
*/
~BatchLoader();
// -------------------------------------------------------------------
/** Sets the validation step. True for enable validation during postprocess.
* @param enable True for validation.
*/
void setValidation( bool enabled );
// -------------------------------------------------------------------
/** Returns the current validation step.
* @return The current validation step.
*/
bool getValidation() const;
// -------------------------------------------------------------------
/** Add a new file to the list of files to be loaded.
* @param file File to be loaded
* @param steps Post-processing steps to be executed on the file
* @param map Optional configuration properties
* @return 'Load request channel' - an unique ID that can later
* be used to access the imported file data.
* @see GetImport */
unsigned int AddLoadRequest (
const std::string& file,
unsigned int steps = 0,
const PropertyMap* map = NULL
);
// -------------------------------------------------------------------
/** Get an imported scene.
* This polls the import from the internal request list.
* If an import is requested several times, this function
* can be called several times, too.
*
* @param which LRWC returned by AddLoadRequest().
* @return NULL if there is no scene with this file name
* in the queue of the scene hasn't been loaded yet. */
aiScene* GetImport(
unsigned int which
);
// -------------------------------------------------------------------
/** Waits until all scenes have been loaded. This returns
* immediately if no scenes are queued.*/
void LoadAll();
private:
// No need to have that in the public API ...
BatchData *m_data;
};
} // Namespace Assimp
#endif // INCLUDED_AI_IMPORTER_H

View File

@ -1,377 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 ImporterRegistry.cpp
Central registry for all importers available. Do not edit this file
directly (unless you are adding new loaders), instead use the
corresponding preprocessor flag to selectively disable formats.
*/
#include <vector>
#include <assimp/BaseImporter.h>
// ------------------------------------------------------------------------------------------------
// Importers
// (include_new_importers_here)
// ------------------------------------------------------------------------------------------------
#ifndef ASSIMP_BUILD_NO_X_IMPORTER
# include "X/XFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
# include "AMF/AMFImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
# include "3DS/3DSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
# include "MD3/MD3Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
# include "MDL/MDLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
# include "MD2/MD2Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
# include "Ply/PlyLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
# include "ASE/ASELoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
# include "Obj/ObjFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
# include "HMP/HMPLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
# include "SMD/SMDLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
# include "MDC/MDCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
# include "MD5/MD5Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
# include "STL/STLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
# include "LWO/LWOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
# include "DXF/DXFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
# include "NFF/NFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
# include "Raw/RawLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
# include "SIB/SIBImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
# include "OFF/OFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
# include "AC/ACLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
# include "BVH/BVHLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
# include "Irr/IRRMeshLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
# include "Irr/IRRLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
# include "Q3D/Q3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
# include "B3D/B3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
# include "Collada/ColladaLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
# include "Terragen/TerragenLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
# include "CSM/CSMLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
# include "Unreal/UnrealLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
# include "LWS/LWSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
# include "Ogre/OgreImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
# include "OpenGEX/OpenGEXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
# include "MS3D/MS3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
# include "COB/COBLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
# include "Blender/BlenderLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
# include "Q3BSP/Q3BSPFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
# include "NDO/NDOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
# include "Importer/IFC/IFCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
# include "XGL/XGLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
# include "FBX/FBXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
# include "Assbin/AssbinLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
# include "glTF/glTFImporter.h"
# include "glTF2/glTF2Importer.h"
#endif
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
# include "C4D/C4DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
# include "3MF/D3MFImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
# include "X3D/X3DImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
# include "MMD/MMDImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
# include "M3D/M3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
# include "Importer/StepFile/StepFileImporter.h"
#endif
namespace Assimp {
// ------------------------------------------------------------------------------------------------
void GetImporterInstanceList(std::vector< BaseImporter* >& out)
{
// ----------------------------------------------------------------------------
// Add an instance of each worker class here
// (register_new_importers_here)
// ----------------------------------------------------------------------------
out.reserve(64);
#if (!defined ASSIMP_BUILD_NO_X_IMPORTER)
out.push_back( new XFileImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER)
out.push_back( new ObjFileImporter());
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
out.push_back( new AMFImporter() );
#endif
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back( new Discreet3DSImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_M3D_IMPORTER)
out.push_back( new M3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER)
out.push_back( new MD3Importer());
#endif
#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER)
out.push_back( new MD2Importer());
#endif
#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER)
out.push_back( new PLYImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER)
out.push_back( new MDLImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER)
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back( new ASEImporter());
# endif
#endif
#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER)
out.push_back( new HMPImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER)
out.push_back( new SMDImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER)
out.push_back( new MDCImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER)
out.push_back( new MD5Importer());
#endif
#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER)
out.push_back( new STLImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER)
out.push_back( new LWOImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER)
out.push_back( new DXFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER)
out.push_back( new NFFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER)
out.push_back( new RAWImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER)
out.push_back( new SIBImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER)
out.push_back( new OFFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER)
out.push_back( new AC3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER)
out.push_back( new BVHLoader());
#endif
#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER)
out.push_back( new IRRMeshImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER)
out.push_back( new IRRImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER)
out.push_back( new Q3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER)
out.push_back( new B3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER)
out.push_back( new ColladaLoader());
#endif
#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER)
out.push_back( new TerragenImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER)
out.push_back( new CSMImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER)
out.push_back( new UnrealImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
out.push_back( new LWSImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER)
out.push_back( new Ogre::OgreImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER )
out.push_back( new OpenGEX::OpenGEXImporter() );
#endif
#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER)
out.push_back( new MS3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER)
out.push_back( new COBImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
out.push_back( new BlenderImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
out.push_back( new Q3BSPFileImporter() );
#endif
#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER)
out.push_back( new NDOImporter() );
#endif
#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER)
out.push_back( new IFCImporter() );
#endif
#if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER )
out.push_back( new XGLImporter() );
#endif
#if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER )
out.push_back( new FBXImporter() );
#endif
#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
out.push_back( new AssbinImporter() );
#endif
#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER )
out.push_back( new glTFImporter() );
out.push_back( new glTF2Importer() );
#endif
#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER )
out.push_back( new C4DImporter() );
#endif
#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER )
out.push_back( new D3MFImporter() );
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
out.push_back( new X3DImporter() );
#endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
out.push_back( new MMDImporter() );
#endif
#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
out.push_back(new StepFile::StepFileImporter());
#endif
}
/** will delete all registered importers. */
void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){
for(size_t i= 0; i<deleteList.size();++i){
delete deleteList[i];
deleteList[i]=nullptr;
}//for
}
} // namespace Assimp

View File

@ -1,229 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 PolyTools.h, various utilities for our dealings with arbitrary polygons */
#ifndef AI_POLYTOOLS_H_INCLUDED
#define AI_POLYTOOLS_H_INCLUDED
#include <assimp/material.h>
#include <assimp/ai_assert.h>
namespace Assimp {
// -------------------------------------------------------------------------------
/** Compute the signed area of a triangle.
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline double GetArea2D(const T& v1, const T& v2, const T& v3)
{
return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y));
}
// -------------------------------------------------------------------------------
/** Test if a given point p2 is on the left side of the line formed by p0-p1.
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2)
{
return GetArea2D(p0,p2,p1) > 0;
}
// -------------------------------------------------------------------------------
/** Test if a given point is inside a given triangle in R2.
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp)
{
// Point in triangle test using baryzentric coordinates
const aiVector2D v0 = p1 - p0;
const aiVector2D v1 = p2 - p0;
const aiVector2D v2 = pp - p0;
double dot00 = v0 * v0;
double dot01 = v0 * v1;
double dot02 = v0 * v2;
double dot11 = v1 * v1;
double dot12 = v1 * v2;
const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom;
dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom;
return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1);
}
// -------------------------------------------------------------------------------
/** Check whether the winding order of a given polygon is counter-clockwise.
* The function accepts an unconstrained template parameter, but is intended
* to be used only with aiVector2D and aiVector3D (z axis is ignored, only
* x and y are taken into account).
* @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++
*/
template <typename T>
inline bool IsCCW(T* in, size_t npoints) {
double aa, bb, cc, b, c, theta;
double convex_turn;
double convex_sum = 0;
ai_assert(npoints >= 3);
for (size_t i = 0; i < npoints - 2; i++) {
aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) +
((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y));
bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) +
((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y));
cc = ((in[i+2].x - in[i+1].x) *
(in[i+2].x - in[i+1].x)) +
((-in[i+2].y + in[i+1].y) *
(-in[i+2].y + in[i+1].y));
b = std::sqrt(bb);
c = std::sqrt(cc);
theta = std::acos((bb + cc - aa) / (2 * b * c));
if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) {
// if (convex(in[i].x, in[i].y,
// in[i+1].x, in[i+1].y,
// in[i+2].x, in[i+2].y)) {
convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn;
}
else {
convex_sum -= AI_MATH_PI_F - theta;
}
}
aa = ((in[1].x - in[npoints-2].x) *
(in[1].x - in[npoints-2].x)) +
((-in[1].y + in[npoints-2].y) *
(-in[1].y + in[npoints-2].y));
bb = ((in[0].x - in[npoints-2].x) *
(in[0].x - in[npoints-2].x)) +
((-in[0].y + in[npoints-2].y) *
(-in[0].y + in[npoints-2].y));
cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) +
((-in[1].y + in[0].y) * (-in[1].y + in[0].y));
b = std::sqrt(bb);
c = std::sqrt(cc);
theta = std::acos((bb + cc - aa) / (2 * b * c));
//if (convex(in[npoints-2].x, in[npoints-2].y,
// in[0].x, in[0].y,
// in[1].x, in[1].y)) {
if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) {
convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn;
}
else {
convex_sum -= AI_MATH_PI_F - theta;
}
return convex_sum >= (2 * AI_MATH_PI_F);
}
// -------------------------------------------------------------------------------
/** Compute the normal of an arbitrary polygon in R3.
*
* The code is based on Newell's formula, that is a polygons normal is the ratio
* of its area when projected onto the three coordinate axes.
*
* @param out Receives the output normal
* @param num Number of input vertices
* @param x X data source. x[ofs_x*n] is the n'th element.
* @param y Y data source. y[ofs_y*n] is the y'th element
* @param z Z data source. z[ofs_z*n] is the z'th element
*
* @note The data arrays must have storage for at least num+2 elements. Using
* this method is much faster than the 'other' NewellNormal()
*/
template <int ofs_x, int ofs_y, int ofs_z, typename TReal>
inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z)
{
// Duplicate the first two vertices at the end
x[(num+0)*ofs_x] = x[0];
x[(num+1)*ofs_x] = x[ofs_x];
y[(num+0)*ofs_y] = y[0];
y[(num+1)*ofs_y] = y[ofs_y];
z[(num+0)*ofs_z] = z[0];
z[(num+1)*ofs_z] = z[ofs_z];
TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2;
for (int tmp=0; tmp < num; tmp++) {
sum_xy += (*xptr) * ( (*yhigh) - (*ylow) );
sum_yz += (*yptr) * ( (*zhigh) - (*zlow) );
sum_zx += (*zptr) * ( (*xhigh) - (*xlow) );
xptr += ofs_x;
xlow += ofs_x;
xhigh += ofs_x;
yptr += ofs_y;
ylow += ofs_y;
yhigh += ofs_y;
zptr += ofs_z;
zlow += ofs_z;
zhigh += ofs_z;
}
out = aiVector3t<TReal>(sum_yz,sum_zx,sum_xy);
}
} // ! Assimp
#endif

View File

@ -1,265 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 ImporterRegistry.cpp
Central registry for all postprocessing steps available. Do not edit this file
directly (unless you are adding new steps), instead use the
corresponding preprocessor flag to selectively disable steps.
*/
#include "PostProcessing/ProcessHelper.h"
#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS
# include "PostProcessing/CalcTangentsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
# include "PostProcessing/JoinVerticesProcess.h"
#endif
#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
# include "PostProcessing/ConvertToLHProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
# include "PostProcessing/TriangulateProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS
# include "PostProcessing/DropFaceNormalsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS
# include "PostProcessing/GenFaceNormalsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS
# include "PostProcessing/GenVertexNormalsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS
# include "PostProcessing/RemoveVCProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS
# include "PostProcessing/SplitLargeMeshes.h"
#endif
#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS
# include "PostProcessing/PretransformVertices.h"
#endif
#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS
# include "PostProcessing/LimitBoneWeightsProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
# include "PostProcessing/ValidateDataStructure.h"
#endif
#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS
# include "PostProcessing/ImproveCacheLocality.h"
#endif
#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS
# include "PostProcessing/FixNormalsStep.h"
#endif
#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
# include "PostProcessing/RemoveRedundantMaterials.h"
#endif
#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
# include "PostProcessing/EmbedTexturesProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
# include "PostProcessing/FindInvalidDataProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS
# include "PostProcessing/FindDegenerates.h"
#endif
#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS
# include "PostProcessing/SortByPTypeProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
# include "PostProcessing/ComputeUVMappingProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
# include "PostProcessing/TextureTransform.h"
#endif
#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS
# include "PostProcessing/FindInstancesProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
# include "PostProcessing/OptimizeMeshes.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
# include "PostProcessing/OptimizeGraph.h"
#endif
#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
# include "Common/SplitByBoneCountProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
# include "PostProcessing/DeboneProcess.h"
#endif
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
# include "PostProcessing/ScaleProcess.h"
#endif
#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
# include "PostProcessing/ArmaturePopulate.h"
#endif
#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
# include "PostProcessing/GenBoundingBoxesProcess.h"
#endif
namespace Assimp {
// ------------------------------------------------------------------------------------------------
void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
{
// ----------------------------------------------------------------------------
// Add an instance of each post processing step here in the order
// of sequence it is executed. Steps that are added here are not
// validated - as RegisterPPStep() does - all dependencies must be given.
// ----------------------------------------------------------------------------
out.reserve(31);
#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS)
out.push_back( new MakeLeftHandedProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS)
out.push_back( new FlipUVsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
out.push_back( new FlipWindingOrderProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS)
out.push_back( new RemoveVCProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
out.push_back( new RemoveRedundantMatsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
out.push_back( new EmbedTexturesProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS)
out.push_back( new FindInstancesProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
out.push_back( new OptimizeGraphProcess());
#endif
#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
out.push_back( new ComputeUVMappingProcess());
#endif
#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
out.push_back( new TextureTransformStep());
#endif
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
out.push_back( new ScaleProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
out.push_back( new ArmaturePopulate());
#endif
#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
out.push_back( new PretransformVertices());
#endif
#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS)
out.push_back( new TriangulateProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS)
//find degenerates should run after triangulation (to sort out small
//generated triangles) but before sort by p types (in case there are lines
//and points generated and inserted into a mesh)
out.push_back( new FindDegeneratesProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS)
out.push_back( new SortByPTypeProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS)
out.push_back( new FindInvalidDataProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS)
out.push_back( new OptimizeMeshesProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS)
out.push_back( new FixInfacingNormalsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS)
out.push_back( new SplitByBoneCountProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
out.push_back( new SplitLargeMeshesProcess_Triangle());
#endif
#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
out.push_back( new DropFaceNormalsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
out.push_back( new GenFaceNormalsProcess());
#endif
// .........................................................................
// DON'T change the order of these five ..
// XXX this is actually a design weakness that dates back to the time
// when Importer would maintain the postprocessing step list exclusively.
// Now that others access it too, we need a better solution.
out.push_back( new ComputeSpatialSortProcess());
// .........................................................................
#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS)
out.push_back( new GenVertexNormalsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS)
out.push_back( new CalcTangentsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS)
out.push_back( new JoinVerticesProcess());
#endif
// .........................................................................
out.push_back( new DestroySpatialSortProcess());
// .........................................................................
#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
out.push_back( new SplitLargeMeshesProcess_Vertex());
#endif
#if (!defined ASSIMP_BUILD_NO_DEBONE_PROCESS)
out.push_back( new DeboneProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
out.push_back( new LimitBoneWeightsProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS)
out.push_back( new ImproveCacheLocalityProcess());
#endif
#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
out.push_back(new GenBoundingBoxesProcess);
#endif
}
}

View File

@ -1,113 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 RemoveComments.cpp
* @brief Defines the CommentRemover utility class
*/
#include <assimp/RemoveComments.h>
#include <assimp/ParsingUtils.h>
namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Remove line comments from a file
void CommentRemover::RemoveLineComments(const char* szComment,
char* szBuffer, char chReplacement /* = ' ' */)
{
// validate parameters
ai_assert(NULL != szComment && NULL != szBuffer && *szComment);
const size_t len = strlen(szComment);
while (*szBuffer) {
// skip over quotes
if (*szBuffer == '\"' || *szBuffer == '\'')
while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
if (!strncmp(szBuffer,szComment,len)) {
while (!IsLineEnd(*szBuffer))
*szBuffer++ = chReplacement;
if (!*szBuffer) {
break;
}
}
++szBuffer;
}
}
// ------------------------------------------------------------------------------------------------
// Remove multi-line comments from a file
void CommentRemover::RemoveMultiLineComments(const char* szCommentStart,
const char* szCommentEnd,char* szBuffer,
char chReplacement)
{
// validate parameters
ai_assert(NULL != szCommentStart && NULL != szCommentEnd &&
NULL != szBuffer && *szCommentStart && *szCommentEnd);
const size_t len = strlen(szCommentEnd);
const size_t len2 = strlen(szCommentStart);
while (*szBuffer) {
// skip over quotes
if (*szBuffer == '\"' || *szBuffer == '\'')
while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
if (!strncmp(szBuffer,szCommentStart,len2)) {
while (*szBuffer) {
if (!::strncmp(szBuffer,szCommentEnd,len)) {
for (unsigned int i = 0; i < len;++i)
*szBuffer++ = chReplacement;
break;
}
*szBuffer++ = chReplacement;
}
continue;
}
++szBuffer;
}
}
} // !! Assimp

View File

@ -1,168 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Implementation of the helper class to quickly find
vertices close to a given position. Special implementation for
the 3ds loader handling smooth groups correctly */
#include <assimp/SGSpatialSort.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
SGSpatialSort::SGSpatialSort()
{
// define the reference plane. We choose some arbitrary vector away from all basic axises
// in the hope that no model spreads all its vertices along this plane.
mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
mPlaneNormal.Normalize();
}
// ------------------------------------------------------------------------------------------------
// Destructor
SGSpatialSort::~SGSpatialSort()
{
// nothing to do here, everything destructs automatically
}
// ------------------------------------------------------------------------------------------------
void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index,
unsigned int smoothingGroup)
{
// store position by index and distance
float distance = vPosition * mPlaneNormal;
mPositions.push_back( Entry( index, vPosition,
distance, smoothingGroup));
}
// ------------------------------------------------------------------------------------------------
void SGSpatialSort::Prepare()
{
// now sort the array ascending by distance.
std::sort( this->mPositions.begin(), this->mPositions.end());
}
// ------------------------------------------------------------------------------------------------
// Returns an iterator for all positions close to the given position.
void SGSpatialSort::FindPositions( const aiVector3D& pPosition,
uint32_t pSG,
float pRadius,
std::vector<unsigned int>& poResults,
bool exactMatch /*= false*/) const
{
float dist = pPosition * mPlaneNormal;
float minDist = dist - pRadius, maxDist = dist + pRadius;
// clear the array
poResults.clear();
// quick check for positions outside the range
if( mPositions.empty() )
return;
if( maxDist < mPositions.front().mDistance)
return;
if( minDist > mPositions.back().mDistance)
return;
// do a binary search for the minimal distance to start the iteration there
unsigned int index = (unsigned int)mPositions.size() / 2;
unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
while( binaryStepSize > 1)
{
if( mPositions[index].mDistance < minDist)
index += binaryStepSize;
else
index -= binaryStepSize;
binaryStepSize /= 2;
}
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && mPositions[index].mDistance > minDist)
index--;
while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
index++;
// Mow start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the given radius to the result aray
float squareEpsilon = pRadius * pRadius;
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
std::vector<Entry>::const_iterator end = mPositions.end();
if (exactMatch)
{
while( it->mDistance < maxDist)
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG)
{
poResults.push_back( it->mIndex);
}
++it;
if( end == it )break;
}
}
else
{
// if the given smoothing group is 0, we'll return all surrounding vertices
if (!pSG)
{
while( it->mDistance < maxDist)
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
poResults.push_back( it->mIndex);
++it;
if( end == it)break;
}
}
else while( it->mDistance < maxDist)
{
if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
(it->mSmoothGroups & pSG || !it->mSmoothGroups))
{
poResults.push_back( it->mIndex);
}
++it;
if( end == it)break;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,261 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
----------------------------------------------------------------------
*/
#include "ScenePreprocessor.h"
#include <assimp/ai_assert.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
// ---------------------------------------------------------------------------------------------
void ScenePreprocessor::ProcessScene ()
{
ai_assert(scene != NULL);
// Process all meshes
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
ProcessMesh(scene->mMeshes[i]);
// - nothing to do for nodes for the moment
// - nothing to do for textures for the moment
// - nothing to do for lights for the moment
// - nothing to do for cameras for the moment
// Process all animations
for (unsigned int i = 0; i < scene->mNumAnimations;++i)
ProcessAnimation(scene->mAnimations[i]);
// Generate a default material if none was specified
if (!scene->mNumMaterials && scene->mNumMeshes) {
scene->mMaterials = new aiMaterial*[2];
aiMaterial* helper;
aiString name;
scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial();
aiColor3D clr(0.6f,0.6f,0.6f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
// setup the default name to make this material identifiable
name.Set(AI_DEFAULT_MATERIAL_NAME);
helper->AddProperty(&name,AI_MATKEY_NAME);
ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'");
for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials;
}
scene->mNumMaterials++;
}
}
// ---------------------------------------------------------------------------------------------
void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
{
// If aiMesh::mNumUVComponents is *not* set assign the default value of 2
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (!mesh->mTextureCoords[i]) {
mesh->mNumUVComponents[i] = 0;
} else {
if (!mesh->mNumUVComponents[i])
mesh->mNumUVComponents[i] = 2;
aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices;
// Ensure unused components are zeroed. This will make 1D texture channels work
// as if they were 2D channels .. just in case an application doesn't handle
// this case
if (2 == mesh->mNumUVComponents[i]) {
for (; p != end; ++p)
p->z = 0.f;
}
else if (1 == mesh->mNumUVComponents[i]) {
for (; p != end; ++p)
p->z = p->y = 0.f;
}
else if (3 == mesh->mNumUVComponents[i]) {
// Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
for (; p != end; ++p) {
if (p->z != 0)
break;
}
if (p == end) {
ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
mesh->mNumUVComponents[i] = 2;
}
}
}
}
// If the information which primitive types are there in the
// mesh is currently not available, compute it.
if (!mesh->mPrimitiveTypes) {
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace& face = mesh->mFaces[a];
switch (face.mNumIndices)
{
case 3u:
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
case 2u:
mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
break;
case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
default:
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break;
}
}
}
// If tangents and normals are given but no bitangents compute them
if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) {
mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
for (unsigned int i = 0; i < mesh->mNumVertices;++i) {
mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i];
}
}
}
// ---------------------------------------------------------------------------------------------
void ScenePreprocessor::ProcessAnimation (aiAnimation* anim)
{
double first = 10e10, last = -10e10;
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
aiNodeAnim* channel = anim->mChannels[i];
/* If the exact duration of the animation is not given
* compute it now.
*/
if (anim->mDuration == -1.) {
// Position keys
for (unsigned int j = 0; j < channel->mNumPositionKeys;++j) {
aiVectorKey& key = channel->mPositionKeys[j];
first = std::min (first, key.mTime);
last = std::max (last, key.mTime);
}
// Scaling keys
for (unsigned int j = 0; j < channel->mNumScalingKeys;++j ) {
aiVectorKey& key = channel->mScalingKeys[j];
first = std::min (first, key.mTime);
last = std::max (last, key.mTime);
}
// Rotation keys
for (unsigned int j = 0; j < channel->mNumRotationKeys;++j ) {
aiQuatKey& key = channel->mRotationKeys[ j ];
first = std::min (first, key.mTime);
last = std::max (last, key.mTime);
}
}
/* Check whether the animation channel has no rotation
* or position tracks. In this case we generate a dummy
* track from the information we have in the transformation
* matrix of the corresponding node.
*/
if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) {
// Find the node that belongs to this animation
aiNode* node = scene->mRootNode->FindNode(channel->mNodeName);
if (node) // ValidateDS will complain later if 'node' is NULL
{
// Decompose the transformation matrix of the node
aiVector3D scaling, position;
aiQuaternion rotation;
node->mTransformation.Decompose(scaling, rotation,position);
// No rotation keys? Generate a dummy track
if (!channel->mNumRotationKeys) {
channel->mNumRotationKeys = 1;
channel->mRotationKeys = new aiQuatKey[1];
aiQuatKey& q = channel->mRotationKeys[0];
q.mTime = 0.;
q.mValue = rotation;
ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy rotation track has been generated");
}
// No scaling keys? Generate a dummy track
if (!channel->mNumScalingKeys) {
channel->mNumScalingKeys = 1;
channel->mScalingKeys = new aiVectorKey[1];
aiVectorKey& q = channel->mScalingKeys[0];
q.mTime = 0.;
q.mValue = scaling;
ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy scaling track has been generated");
}
// No position keys? Generate a dummy track
if (!channel->mNumPositionKeys) {
channel->mNumPositionKeys = 1;
channel->mPositionKeys = new aiVectorKey[1];
aiVectorKey& q = channel->mPositionKeys[0];
q.mTime = 0.;
q.mValue = position;
ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy position track has been generated");
}
}
}
}
if (anim->mDuration == -1.) {
ASSIMP_LOG_DEBUG("ScenePreprocessor: Setting animation duration");
anim->mDuration = last - std::min( first, 0. );
}
}

View File

@ -1,125 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Defines a post processing step to search all meshes for
degenerated faces */
#ifndef AI_SCENE_PREPROCESSOR_H_INC
#define AI_SCENE_PREPROCESSOR_H_INC
#include <assimp/defs.h>
#include <stddef.h>
struct aiScene;
struct aiAnimation;
struct aiMesh;
class ScenePreprocessorTest;
namespace Assimp {
// ----------------------------------------------------------------------------------
/** ScenePreprocessor: Preprocess a scene before any post-processing
* steps are executed.
*
* The step computes data that needn't necessarily be provided by the
* importer, such as aiMesh::mPrimitiveTypes.
*/
// ----------------------------------------------------------------------------------
class ASSIMP_API ScenePreprocessor
{
// Make ourselves a friend of the corresponding test unit.
friend class ::ScenePreprocessorTest;
public:
// ----------------------------------------------------------------
/** Default c'tpr. Use SetScene() to assign a scene to the object.
*/
ScenePreprocessor()
: scene (NULL)
{}
/** Constructs the object and assigns a specific scene to it
*/
ScenePreprocessor(aiScene* _scene)
: scene (_scene)
{}
// ----------------------------------------------------------------
/** Assign a (new) scene to the object.
*
* One 'SceneProcessor' can be used for multiple scenes.
* Call ProcessScene to have the scene preprocessed.
* @param sc Scene to be processed.
*/
void SetScene (aiScene* sc) {
scene = sc;
}
// ----------------------------------------------------------------
/** Preprocess the current scene
*/
void ProcessScene ();
protected:
// ----------------------------------------------------------------
/** Preprocess an animation in the scene
* @param anim Anim to be preprocessed.
*/
void ProcessAnimation (aiAnimation* anim);
// ----------------------------------------------------------------
/** Preprocess a mesh in the scene
* @param mesh Mesh to be preprocessed.
*/
void ProcessMesh (aiMesh* mesh);
protected:
//! Scene we're currently working on
aiScene* scene;
};
} // ! end namespace Assimp
#endif // include guard

View File

@ -1,105 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Stuff to deal with aiScene::mPrivate
*/
#pragma once
#ifndef AI_SCENEPRIVATE_H_INCLUDED
#define AI_SCENEPRIVATE_H_INCLUDED
#include <assimp/ai_assert.h>
#include <assimp/scene.h>
namespace Assimp {
// Forward declarations
class Importer;
struct ScenePrivateData {
// The struct constructor.
ScenePrivateData() AI_NO_EXCEPT;
// Importer that originally loaded the scene though the C-API
// If set, this object is owned by this private data instance.
Assimp::Importer* mOrigImporter;
// List of post-processing steps already applied to the scene.
unsigned int mPPStepsApplied;
// true if the scene is a copy made with aiCopyScene()
// or the corresponding C++ API. This means that user code
// may have made modifications to it, so mPPStepsApplied
// and mOrigImporter are no longer safe to rely on and only
// serve informative purposes.
bool mIsCopy;
};
inline
ScenePrivateData::ScenePrivateData() AI_NO_EXCEPT
: mOrigImporter( nullptr )
, mPPStepsApplied( 0 )
, mIsCopy( false ) {
// empty
}
// Access private data stored in the scene
inline
ScenePrivateData* ScenePriv(aiScene* in) {
ai_assert( nullptr != in );
if ( nullptr == in ) {
return nullptr;
}
return static_cast<ScenePrivateData*>(in->mPrivate);
}
inline
const ScenePrivateData* ScenePriv(const aiScene* in) {
ai_assert( nullptr != in );
if ( nullptr == in ) {
return nullptr;
}
return static_cast<const ScenePrivateData*>(in->mPrivate);
}
} // Namespace Assimp
#endif // AI_SCENEPRIVATE_H_INCLUDED

View File

@ -1,270 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 SkeletonMeshBuilder.cpp
* @brief Implementation of a little class to construct a dummy mesh for a skeleton
*/
#include <assimp/scene.h>
#include <assimp/SkeletonMeshBuilder.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// The constructor processes the given scene and adds a mesh there.
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly)
{
// nothing to do if there's mesh data already present at the scene
if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
return;
if (!root)
root = pScene->mRootNode;
mKnobsOnly = bKnobsOnly;
// build some faces around each node
CreateGeometry( root );
// create a mesh to hold all the generated faces
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
pScene->mMeshes[0] = CreateMesh();
// and install it at the root node
root->mNumMeshes = 1;
root->mMeshes = new unsigned int[1];
root->mMeshes[0] = 0;
// create a dummy material for the mesh
if(pScene->mNumMaterials==0){
pScene->mNumMaterials = 1;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = CreateMaterial();
}
}
// ------------------------------------------------------------------------------------------------
// Recursively builds a simple mesh representation for the given node
void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
{
// add a joint entry for the node.
const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size());
// now build the geometry.
if( pNode->mNumChildren > 0 && !mKnobsOnly)
{
// If the node has children, we build little pointers to each of them
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
{
// find a suitable coordinate system
const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation;
aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4);
ai_real distanceToChild = childpos.Length();
if( distanceToChild < 0.0001)
continue;
aiVector3D up = aiVector3D( childpos).Normalize();
aiVector3D orth( 1.0, 0.0, 0.0);
if( std::fabs( orth * up) > 0.99)
orth.Set( 0.0, 1.0, 0.0);
aiVector3D front = (up ^ orth).Normalize();
aiVector3D side = (front ^ up).Normalize();
unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size());
mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( front * distanceToChild * (ai_real)0.1);
mVertices.push_back( front * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( side * distanceToChild * (ai_real)0.1);
mVertices.push_back( side * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
}
}
else
{
// if the node has no children, it's an end node. Put a little knob there instead
aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 );
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
}
unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex);
if( numVertices > 0)
{
// create a bone affecting all the newly created vertices
aiBone* bone = new aiBone;
mBones.push_back( bone);
bone->mName = pNode->mName;
// calculate the bone offset matrix by concatenating the inverse transformations of all parents
bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse();
for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent)
bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix;
// add all the vertices to the bone's influences
bone->mNumWeights = numVertices;
bone->mWeights = new aiVertexWeight[numVertices];
for( unsigned int a = 0; a < numVertices; a++)
bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0);
// HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
// them to the array, but I'm tired now and I'm annoyed.
aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse();
for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
mVertices[a] = boneToMeshTransform * mVertices[a];
}
// and finally recurse into the children list
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
CreateGeometry( pNode->mChildren[a]);
}
// ------------------------------------------------------------------------------------------------
// Creates the mesh from the internally accumulated stuff and returns it.
aiMesh* SkeletonMeshBuilder::CreateMesh()
{
aiMesh* mesh = new aiMesh();
// add points
mesh->mNumVertices = static_cast<unsigned int>(mVertices.size());
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
mesh->mNormals = new aiVector3D[mesh->mNumVertices];
// add faces
mesh->mNumFaces = static_cast<unsigned int>(mFaces.size());
mesh->mFaces = new aiFace[mesh->mNumFaces];
for( unsigned int a = 0; a < mesh->mNumFaces; a++)
{
const Face& inface = mFaces[a];
aiFace& outface = mesh->mFaces[a];
outface.mNumIndices = 3;
outface.mIndices = new unsigned int[3];
outface.mIndices[0] = inface.mIndices[0];
outface.mIndices[1] = inface.mIndices[1];
outface.mIndices[2] = inface.mIndices[2];
// Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize
// the skeleton, so it's good if there's a visual difference to the rest of the geometry
aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^
(mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));
if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/
nor = aiVector3D(1.0,0.0,0.0);
for (unsigned int n = 0; n < 3; ++n)
mesh->mNormals[inface.mIndices[n]] = nor;
}
// add the bones
mesh->mNumBones = static_cast<unsigned int>(mBones.size());
mesh->mBones = new aiBone*[mesh->mNumBones];
std::copy( mBones.begin(), mBones.end(), mesh->mBones);
// default
mesh->mMaterialIndex = 0;
return mesh;
}
// ------------------------------------------------------------------------------------------------
// Creates a dummy material and returns it.
aiMaterial* SkeletonMeshBuilder::CreateMaterial()
{
aiMaterial* matHelper = new aiMaterial;
// Name
aiString matName( std::string( "SkeletonMaterial"));
matHelper->AddProperty( &matName, AI_MATKEY_NAME);
// Prevent backface culling
const int no_cull = 1;
matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED);
return matHelper;
}

View File

@ -1,342 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Implementation of the helper class to quickly find vertices close to a given position */
#include <assimp/SpatialSort.h>
#include <assimp/ai_assert.h>
using namespace Assimp;
// CHAR_BIT seems to be defined under MVSC, but not under GCC. Pray that the correct value is 8.
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
// ------------------------------------------------------------------------------------------------
// Constructs a spatially sorted representation from the given position array.
SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions,
unsigned int pElementOffset)
// define the reference plane. We choose some arbitrary vector away from all basic axises
// in the hope that no model spreads all its vertices along this plane.
: mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
{
mPlaneNormal.Normalize();
Fill(pPositions,pNumPositions,pElementOffset);
}
// ------------------------------------------------------------------------------------------------
SpatialSort :: SpatialSort()
: mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
{
mPlaneNormal.Normalize();
}
// ------------------------------------------------------------------------------------------------
// Destructor
SpatialSort::~SpatialSort()
{
// nothing to do here, everything destructs automatically
}
// ------------------------------------------------------------------------------------------------
void SpatialSort::Fill( const aiVector3D* pPositions, unsigned int pNumPositions,
unsigned int pElementOffset,
bool pFinalize /*= true */)
{
mPositions.clear();
Append(pPositions,pNumPositions,pElementOffset,pFinalize);
}
// ------------------------------------------------------------------------------------------------
void SpatialSort :: Finalize()
{
std::sort( mPositions.begin(), mPositions.end());
}
// ------------------------------------------------------------------------------------------------
void SpatialSort::Append( const aiVector3D* pPositions, unsigned int pNumPositions,
unsigned int pElementOffset,
bool pFinalize /*= true */)
{
// store references to all given positions along with their distance to the reference plane
const size_t initial = mPositions.size();
mPositions.reserve(initial + (pFinalize?pNumPositions:pNumPositions*2));
for( unsigned int a = 0; a < pNumPositions; a++)
{
const char* tempPointer = reinterpret_cast<const char*> (pPositions);
const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
// store position by index and distance
ai_real distance = *vec * mPlaneNormal;
mPositions.push_back( Entry( static_cast<unsigned int>(a+initial), *vec, distance));
}
if (pFinalize) {
// now sort the array ascending by distance.
Finalize();
}
}
// ------------------------------------------------------------------------------------------------
// Returns an iterator for all positions close to the given position.
void SpatialSort::FindPositions( const aiVector3D& pPosition,
ai_real pRadius, std::vector<unsigned int>& poResults) const
{
const ai_real dist = pPosition * mPlaneNormal;
const ai_real minDist = dist - pRadius, maxDist = dist + pRadius;
// clear the array
poResults.clear();
// quick check for positions outside the range
if( mPositions.size() == 0)
return;
if( maxDist < mPositions.front().mDistance)
return;
if( minDist > mPositions.back().mDistance)
return;
// do a binary search for the minimal distance to start the iteration there
unsigned int index = (unsigned int)mPositions.size() / 2;
unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
while( binaryStepSize > 1)
{
if( mPositions[index].mDistance < minDist)
index += binaryStepSize;
else
index -= binaryStepSize;
binaryStepSize /= 2;
}
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && mPositions[index].mDistance > minDist)
index--;
while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
index++;
// Mow start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the given radius to the result aray
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
const ai_real pSquared = pRadius*pRadius;
while( it->mDistance < maxDist)
{
if( (it->mPosition - pPosition).SquareLength() < pSquared)
poResults.push_back( it->mIndex);
++it;
if( it == mPositions.end())
break;
}
// that's it
}
namespace {
// Binary, signed-integer representation of a single-precision floating-point value.
// IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are
// ordered the same way when their bits are reinterpreted as sign-magnitude integers."
// This allows us to convert all floating-point numbers to signed integers of arbitrary size
// and then use them to work with ULPs (Units in the Last Place, for high-precision
// computations) or to compare them (integer comparisons are faster than floating-point
// comparisons on many platforms).
typedef ai_int BinFloat;
// --------------------------------------------------------------------------------------------
// Converts the bit pattern of a floating-point number to its signed integer representation.
BinFloat ToBinary( const ai_real & pValue) {
// If this assertion fails, signed int is not big enough to store a float on your platform.
// Please correct the declaration of BinFloat a few lines above - but do it in a portable,
// #ifdef'd manner!
static_assert( sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)");
#if defined( _MSC_VER)
// If this assertion fails, Visual C++ has finally moved to ILP64. This means that this
// code has just become legacy code! Find out the current value of _MSC_VER and modify
// the #if above so it evaluates false on the current and all upcoming VC versions (or
// on the current platform, if LP64 or LLP64 are still used on other platforms).
static_assert( sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)");
// This works best on Visual C++, but other compilers have their problems with it.
const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue);
#else
// On many compilers, reinterpreting a float address as an integer causes aliasing
// problems. This is an ugly but more or less safe way of doing it.
union {
ai_real asFloat;
BinFloat asBin;
} conversion;
conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float)
conversion.asFloat = pValue;
const BinFloat binValue = conversion.asBin;
#endif
// floating-point numbers are of sign-magnitude format, so find out what signed number
// representation we must convert negative values to.
// See http://en.wikipedia.org/wiki/Signed_number_representations.
// Two's complement?
if( (-42 == (~42 + 1)) && (binValue & 0x80000000))
return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue;
// One's complement?
else if ( (-42 == ~42) && (binValue & 0x80000000))
return BinFloat(-0) - binValue;
// Sign-magnitude?
else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary
return binValue;
else
return binValue;
}
} // namespace
// ------------------------------------------------------------------------------------------------
// Fills an array with indices of all positions identical to the given position. In opposite to
// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units.
void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
std::vector<unsigned int>& poResults) const
{
// Epsilons have a huge disadvantage: they are of constant precision, while floating-point
// values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but
// if you apply it to 0.001, it is enormous.
// The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs
// tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of
// logarithmic precision - around 1, they are 1*(2^24) and around 10000, they are 0.00125.
// For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The
// incoming vertex positions might have already been transformed, probably using rather
// inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify
// identical vertex positions.
static const int toleranceInULPs = 4;
// An interesting point is that the inaccuracy grows linear with the number of operations:
// multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs
// plus 0.5 ULPs for the multiplication.
// To compute the distance to the plane, a dot product is needed - that is a multiplication and
// an addition on each number.
static const int distanceToleranceInULPs = toleranceInULPs + 1;
// The squared distance between two 3D vectors is computed the same way, but with an additional
// subtraction.
static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1;
// Convert the plane distance to its signed integer representation so the ULPs tolerance can be
// applied. For some reason, VC won't optimize two calls of the bit pattern conversion.
const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs;
const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs;
// clear the array in this strange fashion because a simple clear() would also deallocate
// the array which we want to avoid
poResults.resize( 0 );
// do a binary search for the minimal distance to start the iteration there
unsigned int index = (unsigned int)mPositions.size() / 2;
unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
while( binaryStepSize > 1)
{
// Ugly, but conditional jumps are faster with integers than with floats
if( minDistBinary > ToBinary(mPositions[index].mDistance))
index += binaryStepSize;
else
index -= binaryStepSize;
binaryStepSize /= 2;
}
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) )
index--;
while( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance))
index++;
// Now start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the tolerance to the result array
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
while( ToBinary(it->mDistance) < maxDistBinary)
{
if( distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength()))
poResults.push_back(it->mIndex);
++it;
if( it == mPositions.end())
break;
}
// that's it
}
// ------------------------------------------------------------------------------------------------
unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int>& fill, ai_real pRadius) const
{
fill.resize(mPositions.size(),UINT_MAX);
ai_real dist, maxDist;
unsigned int t=0;
const ai_real pSquared = pRadius*pRadius;
for (size_t i = 0; i < mPositions.size();) {
dist = mPositions[i].mPosition * mPlaneNormal;
maxDist = dist + pRadius;
fill[mPositions[i].mIndex] = t;
const aiVector3D& oldpos = mPositions[i].mPosition;
for (++i; i < fill.size() && mPositions[i].mDistance < maxDist
&& (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i)
{
fill[mPositions[i].mIndex] = t;
}
++t;
}
#ifdef ASSIMP_BUILD_DEBUG
// debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1
for (size_t i = 0; i < fill.size(); ++i) {
ai_assert(fill[i]<mPositions.size());
}
#endif
return t;
}

View File

@ -1,407 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 SplitByBoneCountProcess.cpp
/// Implementation of the SplitByBoneCount postprocessing step
// internal headers of the post-processing framework
#include "SplitByBoneCountProcess.h"
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
#include <limits>
#include <assimp/TinyFormatter.h>
using namespace Assimp;
using namespace Assimp::Formatter;
// ------------------------------------------------------------------------------------------------
// Constructor
SplitByBoneCountProcess::SplitByBoneCountProcess()
{
// set default, might be overridden by importer config
mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
}
// ------------------------------------------------------------------------------------------------
// Destructor
SplitByBoneCountProcess::~SplitByBoneCountProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag.
bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
{
return !!(pFlags & aiProcess_SplitByBoneCount);
}
// ------------------------------------------------------------------------------------------------
// Updates internal properties
void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
{
mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SplitByBoneCountProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin");
// early out
bool isNecessary = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
isNecessary = true;
if( !isNecessary )
{
ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." );
return;
}
// we need to do something. Let's go.
mSubMeshIndices.clear();
mSubMeshIndices.resize( pScene->mNumMeshes);
// build a new array of meshes for the scene
std::vector<aiMesh*> meshes;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
{
aiMesh* srcMesh = pScene->mMeshes[a];
std::vector<aiMesh*> newMeshes;
SplitMesh( pScene->mMeshes[a], newMeshes);
// mesh was split
if( !newMeshes.empty() )
{
// store new meshes and indices of the new meshes
for( unsigned int b = 0; b < newMeshes.size(); ++b)
{
mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
meshes.push_back( newMeshes[b]);
}
// and destroy the source mesh. It should be completely contained inside the new submeshes
delete srcMesh;
}
else
{
// Mesh is kept unchanged - store it's new place in the mesh array
mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
meshes.push_back( srcMesh);
}
}
// rebuild the scene's mesh array
pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
delete [] pScene->mMeshes;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
UpdateNode( pScene->mRootNode);
ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." );
}
// ------------------------------------------------------------------------------------------------
// Splits the given mesh by bone count.
void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
{
// skip if not necessary
if( pMesh->mNumBones <= mMaxBoneCount )
return;
// necessary optimisation: build a list of all affecting bones for each vertex
// TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
typedef std::pair<unsigned int, float> BoneWeight;
std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
for( unsigned int a = 0; a < pMesh->mNumBones; ++a)
{
const aiBone* bone = pMesh->mBones[a];
for( unsigned int b = 0; b < bone->mNumWeights; ++b)
vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
}
unsigned int numFacesHandled = 0;
std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
while( numFacesHandled < pMesh->mNumFaces )
{
// which bones are used in the current submesh
unsigned int numBones = 0;
std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
// indices of the faces which are going to go into this submesh
std::vector<unsigned int> subMeshFaces;
subMeshFaces.reserve( pMesh->mNumFaces);
// accumulated vertex count of all the faces in this submesh
unsigned int numSubMeshVertices = 0;
// a small local array of new bones for the current face. State of all used bones for that face
// can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
std::vector<unsigned int> newBonesAtCurrentFace;
// add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
for( unsigned int a = 0; a < pMesh->mNumFaces; ++a)
{
// skip if the face is already stored in a submesh
if( isFaceHandled[a] )
continue;
const aiFace& face = pMesh->mFaces[a];
// check every vertex if its bones would still fit into the current submesh
for( unsigned int b = 0; b < face.mNumIndices; ++b )
{
const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
for( unsigned int c = 0; c < vb.size(); ++c)
{
unsigned int boneIndex = vb[c].first;
// if the bone is already used in this submesh, it's ok
if( isBoneUsed[boneIndex] )
continue;
// if it's not used, yet, we would need to add it. Store its bone index
if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() )
newBonesAtCurrentFace.push_back( boneIndex);
}
}
// leave out the face if the new bones required for this face don't fit the bone count limit anymore
if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
continue;
// mark all new bones as necessary
while( !newBonesAtCurrentFace.empty() )
{
unsigned int newIndex = newBonesAtCurrentFace.back();
newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear()
if( isBoneUsed[newIndex] )
continue;
isBoneUsed[newIndex] = true;
numBones++;
}
// store the face index and the vertex count
subMeshFaces.push_back( a);
numSubMeshVertices += face.mNumIndices;
// remember that this face is handled
isFaceHandled[a] = true;
numFacesHandled++;
}
// create a new mesh to hold this subset of the source mesh
aiMesh* newMesh = new aiMesh;
if( pMesh->mName.length > 0 )
newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size());
newMesh->mMaterialIndex = pMesh->mMaterialIndex;
newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
poNewMeshes.push_back( newMesh);
// create all the arrays for this mesh if the old mesh contained them
newMesh->mNumVertices = numSubMeshVertices;
newMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
if( pMesh->HasNormals() )
newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
if( pMesh->HasTangentsAndBitangents() )
{
newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{
if( pMesh->HasTextureCoords( a) )
newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
{
if( pMesh->HasVertexColors( a) )
newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
}
// and copy over the data, generating faces with linear indices along the way
newMesh->mFaces = new aiFace[subMeshFaces.size()];
unsigned int nvi = 0; // next vertex index
std::vector<unsigned int> previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
for( unsigned int a = 0; a < subMeshFaces.size(); ++a )
{
const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
aiFace& dstFace = newMesh->mFaces[a];
dstFace.mNumIndices = srcFace.mNumIndices;
dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
// accumulate linearly all the vertices of the source face
for( unsigned int b = 0; b < dstFace.mNumIndices; ++b )
{
unsigned int srcIndex = srcFace.mIndices[b];
dstFace.mIndices[b] = nvi;
previousVertexIndices[nvi] = srcIndex;
newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
if( pMesh->HasNormals() )
newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
if( pMesh->HasTangentsAndBitangents() )
{
newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
}
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
{
if( pMesh->HasTextureCoords( c) )
newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
}
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
{
if( pMesh->HasVertexColors( c) )
newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
}
nvi++;
}
}
ai_assert( nvi == numSubMeshVertices );
// Create the bones for the new submesh: first create the bone array
newMesh->mNumBones = 0;
newMesh->mBones = new aiBone*[numBones];
std::vector<unsigned int> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<unsigned int>::max());
for( unsigned int a = 0; a < pMesh->mNumBones; ++a )
{
if( !isBoneUsed[a] )
continue;
// create the new bone
const aiBone* srcBone = pMesh->mBones[a];
aiBone* dstBone = new aiBone;
mappedBoneIndex[a] = newMesh->mNumBones;
newMesh->mBones[newMesh->mNumBones++] = dstBone;
dstBone->mName = srcBone->mName;
dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
dstBone->mNumWeights = 0;
}
ai_assert( newMesh->mNumBones == numBones );
// iterate over all new vertices and count which bones affected its old vertex in the source mesh
for( unsigned int a = 0; a < numSubMeshVertices; ++a )
{
unsigned int oldIndex = previousVertexIndices[a];
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b )
{
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
if( newBoneIndex != std::numeric_limits<unsigned int>::max() )
newMesh->mBones[newBoneIndex]->mNumWeights++;
}
}
// allocate all bone weight arrays accordingly
for( unsigned int a = 0; a < newMesh->mNumBones; ++a )
{
aiBone* bone = newMesh->mBones[a];
ai_assert( bone->mNumWeights > 0 );
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
bone->mNumWeights = 0; // for counting up in the next step
}
// now copy all the bone vertex weights for all the vertices which made it into the new submesh
for( unsigned int a = 0; a < numSubMeshVertices; ++a)
{
// find the source vertex for it in the source mesh
unsigned int previousIndex = previousVertexIndices[a];
// these bones were affecting it
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
// all of the bones affecting it should be present in the new submesh, or else
// the face it comprises shouldn't be present
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b)
{
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
ai_assert( newBoneIndex != std::numeric_limits<unsigned int>::max() );
aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
newMesh->mBones[newBoneIndex]->mNumWeights++;
dstWeight->mVertexId = a;
dstWeight->mWeight = bonesOnThisVertex[b].second;
}
}
// I have the strange feeling that this will break apart at some point in time...
}
}
// ------------------------------------------------------------------------------------------------
// Recursively updates the node's mesh list to account for the changed mesh list
void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
{
// rebuild the node's mesh index list
if( pNode->mNumMeshes > 0 )
{
std::vector<unsigned int> newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a)
{
unsigned int srcIndex = pNode->mMeshes[a];
const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex];
newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
}
delete [] pNode->mMeshes;
pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
}
// do that also recursively for all children
for( unsigned int a = 0; a < pNode->mNumChildren; ++a )
{
UpdateNode( pNode->mChildren[a]);
}
}

View File

@ -1,111 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 SplitByBoneCountProcess.h
/// Defines a post processing step to split meshes with many bones into submeshes
#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC
#define AI_SPLITBYBONECOUNTPROCESS_H_INC
#include <vector>
#include "BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
namespace Assimp
{
/** Postprocessing filter to split meshes with many bones into submeshes
* so that each submesh has a certain max bone count.
*
* Applied BEFORE the JoinVertices-Step occurs.
* Returns NON-UNIQUE vertices, splits by bone count.
*/
class SplitByBoneCountProcess : public BaseProcess
{
public:
SplitByBoneCountProcess();
~SplitByBoneCountProcess();
public:
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with. A
* bitwise combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
virtual void SetupProperties(const Importer* pImp);
protected:
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
/// Splits the given mesh by bone count.
/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
void SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const;
/// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode( aiNode* pNode) const;
public:
/// Max bone count. Splitting occurs if a mesh has more than that number of bones.
size_t mMaxBoneCount;
/// Per mesh index: Array of indices of the new submeshes.
std::vector< std::vector<unsigned int> > mSubMeshIndices;
};
} // end of namespace Assimp
#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

View File

@ -1,507 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 StandardShapes.cpp
* @brief Implementation of the StandardShapes class
*
* The primitive geometry data comes from
* http://geometrictools.com/Documentation/PlatonicSolids.pdf.
*/
#include <assimp/StandardShapes.h>
#include <assimp/StringComparison.h>
#include <stddef.h>
#include <assimp/Defines.h>
#include <assimp/mesh.h>
namespace Assimp {
# define ADD_TRIANGLE(n0,n1,n2) \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2);
# define ADD_PENTAGON(n0,n1,n2,n3,n4) \
if (polygons) \
{ \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2); \
positions.push_back(n3); \
positions.push_back(n4); \
} \
else \
{ \
ADD_TRIANGLE(n0, n1, n2) \
ADD_TRIANGLE(n0, n2, n3) \
ADD_TRIANGLE(n0, n3, n4) \
}
# define ADD_QUAD(n0,n1,n2,n3) \
if (polygons) \
{ \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2); \
positions.push_back(n3); \
} \
else \
{ \
ADD_TRIANGLE(n0, n1, n2) \
ADD_TRIANGLE(n0, n2, n3) \
}
// ------------------------------------------------------------------------------------------------
// Fast subdivision for a mesh whose verts have a magnitude of 1
void Subdivide(std::vector<aiVector3D>& positions)
{
// assume this to be constant - (fixme: must be 1.0? I think so)
const ai_real fl1 = positions[0].Length();
unsigned int origSize = (unsigned int)positions.size();
for (unsigned int i = 0 ; i < origSize ; i+=3)
{
aiVector3D& tv0 = positions[i];
aiVector3D& tv1 = positions[i+1];
aiVector3D& tv2 = positions[i+2];
aiVector3D a = tv0, b = tv1, c = tv2;
aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1;
aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1;
aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1;
tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original
ADD_TRIANGLE(v1, v2, a);
ADD_TRIANGLE(v2, v3, c);
ADD_TRIANGLE(v3, v1, b);
}
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh from given vertex positions
aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
unsigned int numIndices)
{
if (positions.empty() || !numIndices) return NULL;
// Determine which kinds of primitives the mesh consists of
aiMesh* out = new aiMesh();
switch (numIndices) {
case 1:
out->mPrimitiveTypes = aiPrimitiveType_POINT;
break;
case 2:
out->mPrimitiveTypes = aiPrimitiveType_LINE;
break;
case 3:
out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
break;
default:
out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
break;
};
out->mNumFaces = (unsigned int)positions.size() / numIndices;
out->mFaces = new aiFace[out->mNumFaces];
for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) {
aiFace& f = out->mFaces[i];
f.mNumIndices = numIndices;
f.mIndices = new unsigned int[numIndices];
for (unsigned int j = 0; i < numIndices; ++i, ++a) {
f.mIndices[j] = a;
}
}
out->mNumVertices = (unsigned int)positions.size();
out->mVertices = new aiVector3D[out->mNumVertices];
::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
return out;
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh with a specific shape (callback)
aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
std::vector<aiVector3D>&))
{
std::vector<aiVector3D> temp;
unsigned num = (*GenerateFunc)(temp);
return MakeMesh(temp,num);
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh with a specific shape (callback)
aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
std::vector<aiVector3D>&, bool))
{
std::vector<aiVector3D> temp;
unsigned num = (*GenerateFunc)(temp,true);
return MakeMesh(temp,num);
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh with a specific shape (callback)
aiMesh* StandardShapes::MakeMesh (unsigned int num, void (*GenerateFunc)(
unsigned int,std::vector<aiVector3D>&))
{
std::vector<aiVector3D> temp;
(*GenerateFunc)(num,temp);
return MakeMesh(temp,3);
}
// ------------------------------------------------------------------------------------------------
// Build an incosahedron with points.magnitude == 1
unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D>& positions)
{
positions.reserve(positions.size()+60);
const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 );
const ai_real s = std::sqrt(ai_real(1.0) + t*t);
const aiVector3D v0 = aiVector3D(t,1.0, 0.0)/s;
const aiVector3D v1 = aiVector3D(-t,1.0, 0.0)/s;
const aiVector3D v2 = aiVector3D(t,-1.0, 0.0)/s;
const aiVector3D v3 = aiVector3D(-t,-1.0, 0.0)/s;
const aiVector3D v4 = aiVector3D(1.0, 0.0, t)/s;
const aiVector3D v5 = aiVector3D(1.0, 0.0,-t)/s;
const aiVector3D v6 = aiVector3D(-1.0, 0.0,t)/s;
const aiVector3D v7 = aiVector3D(-1.0, 0.0,-t)/s;
const aiVector3D v8 = aiVector3D(0.0, t, 1.0)/s;
const aiVector3D v9 = aiVector3D(0.0,-t, 1.0)/s;
const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s;
const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s;
ADD_TRIANGLE(v0,v8,v4);
ADD_TRIANGLE(v0,v5,v10);
ADD_TRIANGLE(v2,v4,v9);
ADD_TRIANGLE(v2,v11,v5);
ADD_TRIANGLE(v1,v6,v8);
ADD_TRIANGLE(v1,v10,v7);
ADD_TRIANGLE(v3,v9,v6);
ADD_TRIANGLE(v3,v7,v11);
ADD_TRIANGLE(v0,v10,v8);
ADD_TRIANGLE(v1,v8,v10);
ADD_TRIANGLE(v2,v9,v11);
ADD_TRIANGLE(v3,v11,v9);
ADD_TRIANGLE(v4,v2,v0);
ADD_TRIANGLE(v5,v0,v2);
ADD_TRIANGLE(v6,v1,v3);
ADD_TRIANGLE(v7,v3,v1);
ADD_TRIANGLE(v8,v6,v4);
ADD_TRIANGLE(v9,v4,v6);
ADD_TRIANGLE(v10,v5,v7);
ADD_TRIANGLE(v11,v7,v5);
return 3;
}
// ------------------------------------------------------------------------------------------------
// Build a dodecahedron with points.magnitude == 1
unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions,
bool polygons /*= false*/)
{
positions.reserve(positions.size()+108);
const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508);
const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) );
const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) );
const aiVector3D v0 = aiVector3D(a,a,a);
const aiVector3D v1 = aiVector3D(a,a,-a);
const aiVector3D v2 = aiVector3D(a,-a,a);
const aiVector3D v3 = aiVector3D(a,-a,-a);
const aiVector3D v4 = aiVector3D(-a,a,a);
const aiVector3D v5 = aiVector3D(-a,a,-a);
const aiVector3D v6 = aiVector3D(-a,-a,a);
const aiVector3D v7 = aiVector3D(-a,-a,-a);
const aiVector3D v8 = aiVector3D(b,c,0.0);
const aiVector3D v9 = aiVector3D(-b,c,0.0);
const aiVector3D v10 = aiVector3D(b,-c,0.0);
const aiVector3D v11 = aiVector3D(-b,-c,0.0);
const aiVector3D v12 = aiVector3D(c, 0.0, b);
const aiVector3D v13 = aiVector3D(c, 0.0, -b);
const aiVector3D v14 = aiVector3D(-c, 0.0, b);
const aiVector3D v15 = aiVector3D(-c, 0.0, -b);
const aiVector3D v16 = aiVector3D(0.0, b, c);
const aiVector3D v17 = aiVector3D(0.0, -b, c);
const aiVector3D v18 = aiVector3D(0.0, b, -c);
const aiVector3D v19 = aiVector3D(0.0, -b, -c);
ADD_PENTAGON(v0, v8, v9, v4, v16);
ADD_PENTAGON(v0, v12, v13, v1, v8);
ADD_PENTAGON(v0, v16, v17, v2, v12);
ADD_PENTAGON(v8, v1, v18, v5, v9);
ADD_PENTAGON(v12, v2, v10, v3, v13);
ADD_PENTAGON(v16, v4, v14, v6, v17);
ADD_PENTAGON(v9, v5, v15, v14, v4);
ADD_PENTAGON(v6, v11, v10, v2, v17);
ADD_PENTAGON(v3, v19, v18, v1, v13);
ADD_PENTAGON(v7, v15, v5, v18, v19);
ADD_PENTAGON(v7, v11, v6, v14, v15);
ADD_PENTAGON(v7, v19, v3, v10, v11);
return (polygons ? 5 : 3);
}
// ------------------------------------------------------------------------------------------------
// Build an octahedron with points.magnitude == 1
unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D>& positions)
{
positions.reserve(positions.size()+24);
const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0) ;
const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0);
const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0);
const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0);
const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0);
const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0);
ADD_TRIANGLE(v4,v0,v2);
ADD_TRIANGLE(v4,v2,v1);
ADD_TRIANGLE(v4,v1,v3);
ADD_TRIANGLE(v4,v3,v0);
ADD_TRIANGLE(v5,v2,v0);
ADD_TRIANGLE(v5,v1,v2);
ADD_TRIANGLE(v5,v3,v1);
ADD_TRIANGLE(v5,v0,v3);
return 3;
}
// ------------------------------------------------------------------------------------------------
// Build a tetrahedron with points.magnitude == 1
unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D>& positions)
{
positions.reserve(positions.size()+9);
const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 );
const ai_real a = ai_real( 1.41421 ) * invThree;
const ai_real b = ai_real( 2.4494 ) * invThree;
const aiVector3D v0 = aiVector3D(0.0,0.0,1.0);
const aiVector3D v1 = aiVector3D(2*a,0,-invThree );
const aiVector3D v2 = aiVector3D(-a,b,-invThree );
const aiVector3D v3 = aiVector3D(-a,-b,-invThree );
ADD_TRIANGLE(v0,v1,v2);
ADD_TRIANGLE(v0,v2,v3);
ADD_TRIANGLE(v0,v3,v1);
ADD_TRIANGLE(v1,v3,v2);
return 3;
}
// ------------------------------------------------------------------------------------------------
// Build a hexahedron with points.magnitude == 1
unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions,
bool polygons /*= false*/)
{
positions.reserve(positions.size()+36);
const ai_real length = ai_real(1.0)/ai_real(1.73205080);
const aiVector3D v0 = aiVector3D(-1.0,-1.0,-1.0)*length;
const aiVector3D v1 = aiVector3D(1.0,-1.0,-1.0)*length;
const aiVector3D v2 = aiVector3D(1.0,1.0,-1.0)*length;
const aiVector3D v3 = aiVector3D(-1.0,1.0,-1.0)*length;
const aiVector3D v4 = aiVector3D(-1.0,-1.0,1.0)*length;
const aiVector3D v5 = aiVector3D(1.0,-1.0,1.0)*length;
const aiVector3D v6 = aiVector3D(1.0,1.0,1.0)*length;
const aiVector3D v7 = aiVector3D(-1.0,1.0,1.0)*length;
ADD_QUAD(v0,v3,v2,v1);
ADD_QUAD(v0,v1,v5,v4);
ADD_QUAD(v0,v4,v7,v3);
ADD_QUAD(v6,v5,v1,v2);
ADD_QUAD(v6,v2,v3,v7);
ADD_QUAD(v6,v7,v4,v5);
return (polygons ? 4 : 3);
}
// Cleanup ...
#undef ADD_TRIANGLE
#undef ADD_QUAD
#undef ADD_PENTAGON
// ------------------------------------------------------------------------------------------------
// Create a subdivision sphere
void StandardShapes::MakeSphere(unsigned int tess,
std::vector<aiVector3D>& positions)
{
// Reserve enough storage. Every subdivision
// splits each triangle in 4, the icosahedron consists of 60 verts
positions.reserve(positions.size()+60 * integer_pow(4, tess));
// Construct an icosahedron to start with
MakeIcosahedron(positions);
// ... and subdivide it until the requested output
// tessellation is reached
for (unsigned int i = 0; i<tess;++i)
Subdivide(positions);
}
// ------------------------------------------------------------------------------------------------
// Build a cone
void StandardShapes::MakeCone(ai_real height,ai_real radius1,
ai_real radius2,unsigned int tess,
std::vector<aiVector3D>& positions,bool bOpen /*= false */)
{
// Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE
if (tess < 3 || !height)
return;
size_t old = positions.size();
// No negative radii
radius1 = std::fabs(radius1);
radius2 = std::fabs(radius2);
ai_real halfHeight = height / ai_real(2.0);
// radius1 is always the smaller one
if (radius2 > radius1)
{
std::swap(radius2,radius1);
halfHeight = -halfHeight;
}
else old = SIZE_MAX;
// Use a large epsilon to check whether the cone is pointy
if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0;
// We will need 3*2 verts per segment + 3*2 verts per segment
// if the cone is closed
const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0);
positions.reserve(positions.size () + mem);
// Now construct all segments
const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
ai_real s = 1.0; // std::cos(angle == 0);
ai_real t = 0.0; // std::sin(angle == 0);
for (ai_real angle = 0.0; angle < angle_max; )
{
const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 );
const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 );
const ai_real next = angle + angle_delta;
ai_real s2 = std::cos(next);
ai_real t2 = std::sin(next);
const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 );
const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 );
positions.push_back(v1);
positions.push_back(v2);
positions.push_back(v3);
positions.push_back(v4);
positions.push_back(v1);
positions.push_back(v3);
if (!bOpen)
{
// generate the end 'cap'
positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 ));
positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 ));
positions.push_back(aiVector3D(0.0, halfHeight, 0.0));
if (radius1)
{
// generate the other end 'cap'
positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 ));
positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 ));
positions.push_back(aiVector3D(0.0, -halfHeight, 0.0));
}
}
s = s2;
t = t2;
angle = next;
}
// Need to flip face order?
if ( SIZE_MAX != old ) {
for (size_t p = old; p < positions.size();p += 3) {
std::swap(positions[p],positions[p+1]);
}
}
}
// ------------------------------------------------------------------------------------------------
// Build a circle
void StandardShapes::MakeCircle(ai_real radius, unsigned int tess,
std::vector<aiVector3D>& positions)
{
// Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE
if (tess < 3 || !radius)
return;
radius = std::fabs(radius);
// We will need 3 vertices per segment
positions.reserve(positions.size()+tess*3);
const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
ai_real s = 1.0; // std::cos(angle == 0);
ai_real t = 0.0; // std::sin(angle == 0);
for (ai_real angle = 0.0; angle < angle_max; )
{
positions.push_back(aiVector3D(s * radius,0.0,t * radius));
angle += angle_delta;
s = std::cos(angle);
t = std::sin(angle);
positions.push_back(aiVector3D(s * radius,0.0,t * radius));
positions.push_back(aiVector3D(0.0,0.0,0.0));
}
}
} // ! Assimp

View File

@ -1,101 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 StdOStreamLogStream.h
* @brief Implementation of StdOStreamLogStream
*/
#ifndef AI_STROSTREAMLOGSTREAM_H_INC
#define AI_STROSTREAMLOGSTREAM_H_INC
#include <assimp/LogStream.hpp>
#include <ostream>
namespace Assimp {
// ---------------------------------------------------------------------------
/** @class StdOStreamLogStream
* @brief Logs into a std::ostream
*/
class StdOStreamLogStream : public LogStream {
public:
/** @brief Construction from an existing std::ostream
* @param _ostream Output stream to be used
*/
explicit StdOStreamLogStream(std::ostream& _ostream);
/** @brief Destructor */
~StdOStreamLogStream();
/** @brief Writer */
void write(const char* message);
private:
std::ostream& mOstream;
};
// ---------------------------------------------------------------------------
// Default constructor
inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream)
: mOstream (_ostream){
// empty
}
// ---------------------------------------------------------------------------
// Default constructor
inline StdOStreamLogStream::~StdOStreamLogStream() {
// empty
}
// ---------------------------------------------------------------------------
// Write method
inline void StdOStreamLogStream::write(const char* message) {
mOstream << message;
mOstream.flush();
}
// ---------------------------------------------------------------------------
} // Namespace Assimp
#endif // guard

View File

@ -1,589 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
----------------------------------------------------------------------
*/
#include <assimp/Subdivision.h>
#include <assimp/SceneCombiner.h>
#include <assimp/SpatialSort.h>
#include <assimp/Vertex.h>
#include <assimp/ai_assert.h>
#include "PostProcessing/ProcessHelper.h"
#include <stdio.h>
using namespace Assimp;
void mydummy() {}
// ------------------------------------------------------------------------------------------------
/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The
* implementation is basing on recursive refinement. Directly evaluating the result is also
* possible and much quicker, but it depends on lengthy matrix lookup tables. */
// ------------------------------------------------------------------------------------------------
class CatmullClarkSubdivider : public Subdivider {
public:
void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
void Subdivide (aiMesh** smesh, size_t nmesh,
aiMesh** out, unsigned int num, bool discard_input);
// ---------------------------------------------------------------------------
/** Intermediate description of an edge between two corners of a polygon*/
// ---------------------------------------------------------------------------
struct Edge
{
Edge()
: ref(0)
{}
Vertex edge_point, midpoint;
unsigned int ref;
};
typedef std::vector<unsigned int> UIntVector;
typedef std::map<uint64_t,Edge> EdgeMap;
// ---------------------------------------------------------------------------
// Hashing function to derive an index into an #EdgeMap from two given
// 'unsigned int' vertex coordinates (!!distinct coordinates - same
// vertex position == same index!!).
// NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4
// and (id[0]>2^32-1 or id[0]>2^32-1).
// MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put
// at the head of every function which is about to use MAKE_EDGE_HASH().
// Reason is that the hash is that hash construction needs to hold the
// invariant id0<id1 to identify an edge - else two hashes would refer
// to the same edge.
// ---------------------------------------------------------------------------
#define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\
(eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u))
#define INIT_EDGE_HASH_TEMPORARIES()\
unsigned int eh_tmp0__, eh_tmp1__;
private:
void InternSubdivide (const aiMesh* const * smesh,
size_t nmesh,aiMesh** out, unsigned int num);
};
// ------------------------------------------------------------------------------------------------
// Construct a subdivider of a specific type
Subdivider* Subdivider::Create (Algorithm algo)
{
switch (algo)
{
case CATMULL_CLARKE:
return new CatmullClarkSubdivider();
};
ai_assert(false);
return NULL; // shouldn't happen
}
// ------------------------------------------------------------------------------------------------
// Call the Catmull Clark subdivision algorithm for one mesh
void CatmullClarkSubdivider::Subdivide (
aiMesh* mesh,
aiMesh*& out,
unsigned int num,
bool discard_input
)
{
ai_assert(mesh != out);
Subdivide(&mesh,1,&out,num,discard_input);
}
// ------------------------------------------------------------------------------------------------
// Call the Catmull Clark subdivision algorithm for multiple meshes
void CatmullClarkSubdivider::Subdivide (
aiMesh** smesh,
size_t nmesh,
aiMesh** out,
unsigned int num,
bool discard_input
)
{
ai_assert( NULL != smesh );
ai_assert( NULL != out );
// course, both regions may not overlap
ai_assert(smesh<out || smesh+nmesh>out+nmesh);
if (!num) {
// No subdivision at all. Need to copy all the meshes .. argh.
if (discard_input) {
for (size_t s = 0; s < nmesh; ++s) {
out[s] = smesh[s];
smesh[s] = NULL;
}
}
else {
for (size_t s = 0; s < nmesh; ++s) {
SceneCombiner::Copy(out+s,smesh[s]);
}
}
return;
}
std::vector<aiMesh*> inmeshes;
std::vector<aiMesh*> outmeshes;
std::vector<unsigned int> maptbl;
inmeshes.reserve(nmesh);
outmeshes.reserve(nmesh);
maptbl.reserve(nmesh);
// Remove pure line and point meshes from the working set to reduce the
// number of edge cases the subdivider is forced to deal with. Line and
// point meshes are simply passed through.
for (size_t s = 0; s < nmesh; ++s) {
aiMesh* i = smesh[s];
// FIX - mPrimitiveTypes might not yet be initialized
if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) {
ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh");
if (discard_input) {
out[s] = i;
smesh[s] = NULL;
}
else {
SceneCombiner::Copy(out+s,i);
}
continue;
}
outmeshes.push_back(NULL);inmeshes.push_back(i);
maptbl.push_back(static_cast<unsigned int>(s));
}
// Do the actual subdivision on the preallocated storage. InternSubdivide
// *always* assumes that enough storage is available, it does not bother
// checking any ranges.
ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size());
if (inmeshes.empty()) {
ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything");
return;
}
InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num);
for (unsigned int i = 0; i < maptbl.size(); ++i) {
ai_assert(nullptr != outmeshes[i]);
out[maptbl[i]] = outmeshes[i];
}
if (discard_input) {
for (size_t s = 0; s < nmesh; ++s) {
delete smesh[s];
}
}
}
// ------------------------------------------------------------------------------------------------
// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further
// optimizations (except we're using some nice LUTs). A description of the algorithm can be found
// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface
//
// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's
// expected total runtime complexity. The implementation is able to work in-place on the same
// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate
// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings).
// Previous data is replaced/deleted then.
// ------------------------------------------------------------------------------------------------
void CatmullClarkSubdivider::InternSubdivide (
const aiMesh* const * smesh,
size_t nmesh,
aiMesh** out,
unsigned int num
)
{
ai_assert(NULL != smesh && NULL != out);
INIT_EDGE_HASH_TEMPORARIES();
// no subdivision requested or end of recursive refinement
if (!num) {
return;
}
UIntVector maptbl;
SpatialSort spatial;
// ---------------------------------------------------------------------
// 0. Offset table to index all meshes continuously, generate a spatially
// sorted representation of all vertices in all meshes.
// ---------------------------------------------------------------------
typedef std::pair<unsigned int,unsigned int> IntPair;
std::vector<IntPair> moffsets(nmesh);
unsigned int totfaces = 0, totvert = 0;
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* mesh = smesh[t];
spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false);
moffsets[t] = IntPair(totfaces,totvert);
totfaces += mesh->mNumFaces;
totvert += mesh->mNumVertices;
}
spatial.Finalize();
const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh));
#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx)
#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx)
// ---------------------------------------------------------------------
// 1. Compute the centroid point for all faces
// ---------------------------------------------------------------------
std::vector<Vertex> centroids(totfaces);
unsigned int nfacesout = 0;
for (size_t t = 0, n = 0; t < nmesh; ++t) {
const aiMesh* mesh = smesh[t];
for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n)
{
const aiFace& face = mesh->mFaces[i];
Vertex& c = centroids[n];
for (unsigned int a = 0; a < face.mNumIndices;++a) {
c += Vertex(mesh,face.mIndices[a]);
}
c /= static_cast<float>(face.mNumIndices);
nfacesout += face.mNumIndices;
}
}
{
// we want edges to go away before the recursive calls so begin a new scope
EdgeMap edges;
// ---------------------------------------------------------------------
// 2. Set each edge point to be the average of all neighbouring
// face points and original points. Every edge exists twice
// if there is a neighboring face.
// ---------------------------------------------------------------------
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* mesh = smesh[t];
for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
const aiFace& face = mesh->mFaces[i];
for (unsigned int p =0; p< face.mNumIndices; ++p) {
const unsigned int id[] = {
face.mIndices[p],
face.mIndices[p==face.mNumIndices-1?0:p+1]
};
const unsigned int mp[] = {
maptbl[FLATTEN_VERTEX_IDX(t,id[0])],
maptbl[FLATTEN_VERTEX_IDX(t,id[1])]
};
Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])];
e.ref++;
if (e.ref<=2) {
if (e.ref==1) { // original points (end points) - add only once
e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]);
e.midpoint *= 0.5f;
}
e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)];
}
}
}
}
// ---------------------------------------------------------------------
// 3. Normalize edge points
// ---------------------------------------------------------------------
{unsigned int bad_cnt = 0;
for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) {
if ((*it).second.ref < 2) {
ai_assert((*it).second.ref);
++bad_cnt;
}
(*it).second.edge_point *= 1.f/((*it).second.ref+2.f);
}
if (bad_cnt) {
// Report the number of bad edges. bad edges are referenced by less than two
// faces in the mesh. They occur at outer model boundaries in non-closed
// shapes.
ASSIMP_LOG_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ",
static_cast<unsigned int>(edges.size()), " edges). ");
}}
// ---------------------------------------------------------------------
// 4. Compute a vertex-face adjacency table. We can't reuse the code
// from VertexTriangleAdjacency because we need the table for multiple
// meshes and out vertex indices need to be mapped to distinct values
// first.
// ---------------------------------------------------------------------
UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); {
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* const minp = smesh[t];
for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
const aiFace& f = minp->mFaces[i];
for (unsigned int n = 0; n < f.mNumIndices; ++n) {
++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]];
}
}
}
unsigned int cur = 0;
for (size_t i = 0; i < cntadjfac.size(); ++i) {
ofsadjvec[i+1] = cur;
cur += cntadjfac[i];
}
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* const minp = smesh[t];
for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
const aiFace& f = minp->mFaces[i];
for (unsigned int n = 0; n < f.mNumIndices; ++n) {
faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i);
}
}
}
// check the other way round for consistency
#ifdef ASSIMP_BUILD_DEBUG
for (size_t t = 0; t < ofsadjvec.size()-1; ++t) {
for (unsigned int m = 0; m < cntadjfac[t]; ++m) {
const unsigned int fidx = faceadjac[ofsadjvec[t]+m];
ai_assert(fidx < totfaces);
for (size_t n = 1; n < nmesh; ++n) {
if (moffsets[n].first > fidx) {
const aiMesh* msh = smesh[--n];
const aiFace& f = msh->mFaces[fidx-moffsets[n].first];
bool haveit = false;
for (unsigned int i = 0; i < f.mNumIndices; ++i) {
if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) {
haveit = true;
break;
}
}
ai_assert(haveit);
if (!haveit) {
ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Index not used");
}
break;
}
}
}
}
#endif
}
#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \
fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx]
typedef std::pair<bool,Vertex> TouchedOVertex;
std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex()));
// ---------------------------------------------------------------------
// 5. Spawn a quad from each face point to the corresponding edge points
// the original points being the fourth quad points.
// ---------------------------------------------------------------------
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* const minp = smesh[t];
aiMesh* const mout = out[t] = new aiMesh();
for (unsigned int a = 0; a < minp->mNumFaces; ++a) {
mout->mNumFaces += minp->mFaces[a].mNumIndices;
}
// We need random access to the old face buffer, so reuse is not possible.
mout->mFaces = new aiFace[mout->mNumFaces];
mout->mNumVertices = mout->mNumFaces*4;
mout->mVertices = new aiVector3D[mout->mNumVertices];
// quads only, keep material index
mout->mPrimitiveTypes = aiPrimitiveType_POLYGON;
mout->mMaterialIndex = minp->mMaterialIndex;
if (minp->HasNormals()) {
mout->mNormals = new aiVector3D[mout->mNumVertices];
}
if (minp->HasTangentsAndBitangents()) {
mout->mTangents = new aiVector3D[mout->mNumVertices];
mout->mBitangents = new aiVector3D[mout->mNumVertices];
}
for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) {
mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices];
mout->mNumUVComponents[i] = minp->mNumUVComponents[i];
}
for(unsigned int i = 0; minp->HasVertexColors(i); ++i) {
mout->mColors[i] = new aiColor4D[mout->mNumVertices];
}
mout->mNumVertices = mout->mNumFaces<<2u;
for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) {
const aiFace& face = minp->mFaces[i];
for (unsigned int a = 0; a < face.mNumIndices;++a) {
// Get a clean new face.
aiFace& faceOut = mout->mFaces[n++];
faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4];
// Spawn a new quadrilateral (ccw winding) for this original point between:
// a) face centroid
centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++);
// b) adjacent edge on the left, seen from the centroid
const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1])
])]; // fixme: replace with mod face.mNumIndices?
// c) adjacent edge on the right, seen from the centroid
const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1])
])]; // fixme: replace with mod face.mNumIndices?
e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++);
e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++);
// d= original point P with distinct index i
// F := 0
// R := 0
// n := 0
// for each face f containing i
// F := F+ centroid of f
// R := R+ midpoint of edge of f from i to i+1
// n := n+1
//
// (F+2R+(n-3)P)/n
const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])];
TouchedOVertex& ov = new_points[org];
if (!ov.first) {
ov.first = true;
const unsigned int* adj; unsigned int cnt;
GET_ADJACENT_FACES_AND_CNT(org,adj,cnt);
if (cnt < 3) {
ov.second = Vertex(minp,face.mIndices[a]);
}
else {
Vertex F,R;
for (unsigned int o = 0; o < cnt; ++o) {
ai_assert(adj[o] < totfaces);
F += centroids[adj[o]];
// adj[0] is a global face index - search the face in the mesh list
const aiMesh* mp = NULL;
size_t nidx;
if (adj[o] < moffsets[0].first) {
mp = smesh[nidx=0];
}
else {
for (nidx = 1; nidx<= nmesh; ++nidx) {
if (nidx == nmesh ||moffsets[nidx].first > adj[o]) {
mp = smesh[--nidx];
break;
}
}
}
ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces);
const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first];
bool haveit = false;
// find our original point in the face
for (unsigned int m = 0; m < f.mNumIndices; ++m) {
if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) {
// add *both* edges. this way, we can be sure that we add
// *all* adjacent edges to R. In a closed shape, every
// edge is added twice - so we simply leave out the
// factor 2.f in the amove formula and get the right
// result.
const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])];
// fixme: replace with mod face.mNumIndices?
const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])];
// fixme: replace with mod face.mNumIndices?
R += c0.midpoint+c1.midpoint;
haveit = true;
break;
}
}
// this invariant *must* hold if the vertex-to-face adjacency table is valid
ai_assert(haveit);
if ( !haveit ) {
ASSIMP_LOG_WARN( "OBJ: no name for material library specified." );
}
}
const float div = static_cast<float>(cnt), divsq = 1.f/(div*div);
ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq;
}
}
ov.second.SortBack(mout,faceOut.mIndices[2]=v++);
}
}
}
} // end of scope for edges, freeing its memory
// ---------------------------------------------------------------------
// 7. Apply the next subdivision step.
// ---------------------------------------------------------------------
if (num != 1) {
std::vector<aiMesh*> tmp(nmesh);
InternSubdivide (out,nmesh,&tmp.front(),num-1);
for (size_t i = 0; i < nmesh; ++i) {
delete out[i];
out[i] = tmp[i];
}
}
}

View File

@ -1,248 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
----------------------------------------------------------------------
*/
#include "TargetAnimation.h"
#include <algorithm>
#include <assimp/ai_assert.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos,
const std::vector<aiVectorKey>* _targetObjPos,
const aiVector3D* defaultObjectPos /*= NULL*/,
const aiVector3D* defaultTargetPos /*= NULL*/)
: reachedEnd (false)
, curTime (-1.)
, objPos (_objPos)
, targetObjPos (_targetObjPos)
, nextObjPos (0)
, nextTargetObjPos(0)
{
// Generate default transformation tracks if necessary
if (!objPos || objPos->empty())
{
defaultObjPos.resize(1);
defaultObjPos.front().mTime = 10e10;
if (defaultObjectPos)
defaultObjPos.front().mValue = *defaultObjectPos;
objPos = & defaultObjPos;
}
if (!targetObjPos || targetObjPos->empty())
{
defaultTargetObjPos.resize(1);
defaultTargetObjPos.front().mTime = 10e10;
if (defaultTargetPos)
defaultTargetObjPos.front().mValue = *defaultTargetPos;
targetObjPos = & defaultTargetObjPos;
}
}
// ------------------------------------------------------------------------------------------------
template <class T>
inline T Interpolate(const T& one, const T& two, ai_real val)
{
return one + (two-one)*val;
}
// ------------------------------------------------------------------------------------------------
void KeyIterator::operator ++()
{
// If we are already at the end of all keyframes, return
if (reachedEnd) {
return;
}
// Now search in all arrays for the time value closest
// to our current position on the time line
double d0,d1;
d0 = objPos->at ( std::min ( nextObjPos, static_cast<unsigned int>(objPos->size()-1)) ).mTime;
d1 = targetObjPos->at( std::min ( nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size()-1)) ).mTime;
// Easiest case - all are identical. In this
// case we don't need to interpolate so we can
// return earlier
if ( d0 == d1 )
{
curTime = d0;
curPosition = objPos->at(nextObjPos).mValue;
curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue;
// increment counters
if (objPos->size() != nextObjPos-1)
++nextObjPos;
if (targetObjPos->size() != nextTargetObjPos-1)
++nextTargetObjPos;
}
// An object position key is closest to us
else if (d0 < d1)
{
curTime = d0;
// interpolate the other
if (1 == targetObjPos->size() || !nextTargetObjPos) {
curTargetPosition = targetObjPos->at(0).mValue;
}
else
{
const aiVectorKey& last = targetObjPos->at(nextTargetObjPos);
const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1);
curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real) (
(curTime-first.mTime) / (last.mTime-first.mTime) ));
}
if (objPos->size() != nextObjPos-1)
++nextObjPos;
}
// A target position key is closest to us
else
{
curTime = d1;
// interpolate the other
if (1 == objPos->size() || !nextObjPos) {
curPosition = objPos->at(0).mValue;
}
else
{
const aiVectorKey& last = objPos->at(nextObjPos);
const aiVectorKey& first = objPos->at(nextObjPos-1);
curPosition = Interpolate(first.mValue, last.mValue, (ai_real) (
(curTime-first.mTime) / (last.mTime-first.mTime)));
}
if (targetObjPos->size() != nextTargetObjPos-1)
++nextTargetObjPos;
}
if (nextObjPos >= objPos->size()-1 &&
nextTargetObjPos >= targetObjPos->size()-1)
{
// We reached the very last keyframe
reachedEnd = true;
}
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetTargetAnimationChannel (
const std::vector<aiVectorKey>* _targetPositions)
{
ai_assert(NULL != _targetPositions);
targetPositions = _targetPositions;
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetMainAnimationChannel (
const std::vector<aiVectorKey>* _objectPositions)
{
ai_assert(NULL != _objectPositions);
objectPositions = _objectPositions;
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetFixedMainAnimationChannel(
const aiVector3D& fixed)
{
objectPositions = NULL; // just to avoid confusion
fixedMain = fixed;
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack)
{
ai_assert(NULL != targetPositions && NULL != distanceTrack);
// TODO: in most cases we won't need the extra array
std::vector<aiVectorKey> real;
std::vector<aiVectorKey>* fill = (distanceTrack == objectPositions ? &real : distanceTrack);
fill->reserve(std::max( objectPositions->size(), targetPositions->size() ));
// Iterate through all object keys and interpolate their values if necessary.
// Then get the corresponding target position, compute the difference
// vector between object and target position. Then compute a rotation matrix
// that rotates the base vector of the object coordinate system at that time
// to match the diff vector.
KeyIterator iter(objectPositions,targetPositions,&fixedMain);
for (;!iter.Finished();++iter)
{
const aiVector3D& position = iter.GetCurPosition();
const aiVector3D& tposition = iter.GetCurTargetPosition();
// diff vector
aiVector3D diff = tposition - position;
ai_real f = diff.Length();
// output distance vector
if (f)
{
fill->push_back(aiVectorKey());
aiVectorKey& v = fill->back();
v.mTime = iter.GetCurTime();
v.mValue = diff;
diff /= f;
}
else
{
// FIXME: handle this
}
// diff is now the vector in which our camera is pointing
}
if (real.size()) {
*distanceTrack = real;
}
}

View File

@ -1,183 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Defines a helper class for the ASE and 3DS loaders to
help them compute camera and spot light animation channels */
#ifndef AI_TARGET_ANIMATION_H_INC
#define AI_TARGET_ANIMATION_H_INC
#include <assimp/anim.h>
#include <vector>
namespace Assimp {
// ---------------------------------------------------------------------------
/** Helper class to iterate through all keys in an animation channel.
*
* Missing tracks are interpolated. This is a helper class for
* TargetAnimationHelper, but it can be freely used for other purposes.
*/
class KeyIterator
{
public:
// ------------------------------------------------------------------
/** Constructs a new key iterator
*
* @param _objPos Object position track. May be NULL.
* @param _targetObjPos Target object position track. May be NULL.
* @param defaultObjectPos Default object position to be used if
* no animated track is available. May be NULL.
* @param defaultTargetPos Default target position to be used if
* no animated track is available. May be NULL.
*/
KeyIterator(const std::vector<aiVectorKey>* _objPos,
const std::vector<aiVectorKey>* _targetObjPos,
const aiVector3D* defaultObjectPos = NULL,
const aiVector3D* defaultTargetPos = NULL);
// ------------------------------------------------------------------
/** Returns true if all keys have been processed
*/
bool Finished() const
{return reachedEnd;}
// ------------------------------------------------------------------
/** Increment the iterator
*/
void operator++();
inline void operator++(int)
{return ++(*this);}
// ------------------------------------------------------------------
/** Getters to retrieve the current state of the iterator
*/
inline const aiVector3D& GetCurPosition() const
{return curPosition;}
inline const aiVector3D& GetCurTargetPosition() const
{return curTargetPosition;}
inline double GetCurTime() const
{return curTime;}
private:
//! Did we reach the end?
bool reachedEnd;
//! Represents the current position of the iterator
aiVector3D curPosition, curTargetPosition;
double curTime;
//! Input tracks and the next key to process
const std::vector<aiVectorKey>* objPos,*targetObjPos;
unsigned int nextObjPos, nextTargetObjPos;
std::vector<aiVectorKey> defaultObjPos,defaultTargetObjPos;
};
// ---------------------------------------------------------------------------
/** Helper class for the 3DS and ASE loaders to compute camera and spot light
* animations.
*
* 3DS and ASE store the differently to Assimp - there is an animation
* channel for the camera/spot light itself and a separate position
* animation channels specifying the position of the camera/spot light
* look-at target */
class TargetAnimationHelper
{
public:
TargetAnimationHelper()
: targetPositions (NULL)
, objectPositions (NULL)
{}
// ------------------------------------------------------------------
/** Sets the target animation channel
*
* This channel specifies the position of the camera/spot light
* target at a specific position.
*
* @param targetPositions Translation channel*/
void SetTargetAnimationChannel (const
std::vector<aiVectorKey>* targetPositions);
// ------------------------------------------------------------------
/** Sets the main animation channel
*
* @param objectPositions Translation channel */
void SetMainAnimationChannel ( const
std::vector<aiVectorKey>* objectPositions);
// ------------------------------------------------------------------
/** Sets the main animation channel to a fixed value
*
* @param fixed Fixed value for the main animation channel*/
void SetFixedMainAnimationChannel(const aiVector3D& fixed);
// ------------------------------------------------------------------
/** Computes final animation channels
* @param distanceTrack Receive camera translation keys ... != NULL. */
void Process( std::vector<aiVectorKey>* distanceTrack );
private:
const std::vector<aiVectorKey>* targetPositions,*objectPositions;
aiVector3D fixedMain;
};
} // ! end namespace Assimp
#endif // include guard

View File

@ -1,181 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
---------------------------------------------------------------------------
*/
// Actually just a dummy, used by the compiler to build the precompiled header.
#include <assimp/version.h>
#include <assimp/scene.h>
#include "ScenePrivate.h"
#include "revision.h"
// --------------------------------------------------------------------------------
// Legal information string - don't remove this.
static const char* LEGAL_INFORMATION =
"Open Asset Import Library (Assimp).\n"
"A free C/C++ library to import various 3D file formats into applications\n\n"
"(c) 2006-2019, assimp team\n"
"License under the terms and conditions of the 3-clause BSD license\n"
"http://assimp.org\n"
;
// ------------------------------------------------------------------------------------------------
// Get legal string
ASSIMP_API const char* aiGetLegalString () {
return LEGAL_INFORMATION;
}
// ------------------------------------------------------------------------------------------------
// Get Assimp minor version
ASSIMP_API unsigned int aiGetVersionMinor () {
return VER_MINOR;
}
// ------------------------------------------------------------------------------------------------
// Get Assimp major version
ASSIMP_API unsigned int aiGetVersionMajor () {
return VER_MAJOR;
}
// ------------------------------------------------------------------------------------------------
// Get flags used for compilation
ASSIMP_API unsigned int aiGetCompileFlags () {
unsigned int flags = 0;
#ifdef ASSIMP_BUILD_BOOST_WORKAROUND
flags |= ASSIMP_CFLAGS_NOBOOST;
#endif
#ifdef ASSIMP_BUILD_SINGLETHREADED
flags |= ASSIMP_CFLAGS_SINGLETHREADED;
#endif
#ifdef ASSIMP_BUILD_DEBUG
flags |= ASSIMP_CFLAGS_DEBUG;
#endif
#ifdef ASSIMP_BUILD_DLL_EXPORT
flags |= ASSIMP_CFLAGS_SHARED;
#endif
#ifdef _STLPORT_VERSION
flags |= ASSIMP_CFLAGS_STLPORT;
#endif
return flags;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API unsigned int aiGetVersionRevision() {
return GitVersion;
}
ASSIMP_API const char *aiGetBranchName() {
return GitBranch;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiScene::aiScene()
: mFlags(0)
, mRootNode(nullptr)
, mNumMeshes(0)
, mMeshes(nullptr)
, mNumMaterials(0)
, mMaterials(nullptr)
, mNumAnimations(0)
, mAnimations(nullptr)
, mNumTextures(0)
, mTextures(nullptr)
, mNumLights(0)
, mLights(nullptr)
, mNumCameras(0)
, mCameras(nullptr)
, mMetaData(nullptr)
, mPrivate(new Assimp::ScenePrivateData()) {
// empty
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiScene::~aiScene() {
// delete all sub-objects recursively
delete mRootNode;
// To make sure we won't crash if the data is invalid it's
// much better to check whether both mNumXXX and mXXX are
// valid instead of relying on just one of them.
if (mNumMeshes && mMeshes)
for( unsigned int a = 0; a < mNumMeshes; a++)
delete mMeshes[a];
delete [] mMeshes;
if (mNumMaterials && mMaterials) {
for (unsigned int a = 0; a < mNumMaterials; ++a ) {
delete mMaterials[ a ];
}
}
delete [] mMaterials;
if (mNumAnimations && mAnimations)
for( unsigned int a = 0; a < mNumAnimations; a++)
delete mAnimations[a];
delete [] mAnimations;
if (mNumTextures && mTextures)
for( unsigned int a = 0; a < mNumTextures; a++)
delete mTextures[a];
delete [] mTextures;
if (mNumLights && mLights)
for( unsigned int a = 0; a < mNumLights; a++)
delete mLights[a];
delete [] mLights;
if (mNumCameras && mCameras)
for( unsigned int a = 0; a < mNumCameras; a++)
delete mCameras[a];
delete [] mCameras;
aiMetadata::Dealloc(mMetaData);
mMetaData = nullptr;
delete static_cast<Assimp::ScenePrivateData*>( mPrivate );
}

View File

@ -1,134 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Implementation of the VertexTriangleAdjacency helper class
*/
// internal headers
#include "VertexTriangleAdjacency.h"
#include <assimp/mesh.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
unsigned int iNumFaces,
unsigned int iNumVertices /*= 0*/,
bool bComputeNumTriangles /*= false*/)
{
// compute the number of referenced vertices if it wasn't specified by the caller
const aiFace* const pcFaceEnd = pcFaces + iNumFaces;
if (!iNumVertices) {
for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) {
ai_assert( nullptr != pcFace );
ai_assert(3 == pcFace->mNumIndices);
iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]);
iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]);
iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]);
}
}
mNumVertices = iNumVertices;
unsigned int* pi;
// allocate storage
if (bComputeNumTriangles) {
pi = mLiveTriangles = new unsigned int[iNumVertices+1];
::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1));
mOffsetTable = new unsigned int[iNumVertices+2]+1;
} else {
pi = mOffsetTable = new unsigned int[iNumVertices+2]+1;
::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1));
mLiveTriangles = NULL; // important, otherwise the d'tor would crash
}
// get a pointer to the end of the buffer
unsigned int* piEnd = pi+iNumVertices;
*piEnd++ = 0u;
// first pass: compute the number of faces referencing each vertex
for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace)
{
unsigned nind = pcFace->mNumIndices;
unsigned * ind = pcFace->mIndices;
if (nind > 0) pi[ind[0]]++;
if (nind > 1) pi[ind[1]]++;
if (nind > 2) pi[ind[2]]++;
}
// second pass: compute the final offset table
unsigned int iSum = 0;
unsigned int* piCurOut = this->mOffsetTable;
for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut) {
unsigned int iLastSum = iSum;
iSum += *piCur;
*piCurOut = iLastSum;
}
pi = this->mOffsetTable;
// third pass: compute the final table
this->mAdjacencyTable = new unsigned int[iSum];
iSum = 0;
for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum) {
unsigned nind = pcFace->mNumIndices;
unsigned * ind = pcFace->mIndices;
if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum;
if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum;
if (nind > 2) mAdjacencyTable[pi[ind[2]]++] = iSum;
}
// fourth pass: undo the offset computations made during the third pass
// We could do this in a separate buffer, but this would be TIMES slower.
--mOffsetTable;
*mOffsetTable = 0u;
}
// ------------------------------------------------------------------------------------------------
VertexTriangleAdjacency::~VertexTriangleAdjacency()
{
// delete allocated storage
delete[] mOffsetTable;
delete[] mAdjacencyTable;
delete[] mLiveTriangles;
}

View File

@ -1,117 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Defines a helper class to compute a vertex-triangle adjacency map */
#ifndef AI_VTADJACENCY_H_INC
#define AI_VTADJACENCY_H_INC
#include "BaseProcess.h"
#include <assimp/types.h>
#include <assimp/ai_assert.h>
struct aiMesh;
struct aiFace;
namespace Assimp {
// --------------------------------------------------------------------------------------------
/** @brief The VertexTriangleAdjacency class computes a vertex-triangle
* adjacency map from a given index buffer.
*
* @note Although it is called #VertexTriangleAdjacency, the current version does also
* support arbitrary polygons. */
// --------------------------------------------------------------------------------------------
class ASSIMP_API VertexTriangleAdjacency {
public:
// ----------------------------------------------------------------------------
/** @brief Construction from an existing index buffer
* @param pcFaces Index buffer
* @param iNumFaces Number of faces in the buffer
* @param iNumVertices Number of referenced vertices. This value
* is computed automatically if 0 is specified.
* @param bComputeNumTriangles If you want the class to compute
* a list containing the number of referenced triangles per vertex
* per vertex - pass true. */
VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces,
unsigned int iNumVertices = 0,
bool bComputeNumTriangles = true);
// ----------------------------------------------------------------------------
/** @brief Destructor */
~VertexTriangleAdjacency();
// ----------------------------------------------------------------------------
/** @brief Get all triangles adjacent to a vertex
* @param iVertIndex Index of the vertex
* @return A pointer to the adjacency list. */
unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const {
ai_assert(iVertIndex < mNumVertices);
return &mAdjacencyTable[ mOffsetTable[iVertIndex]];
}
// ----------------------------------------------------------------------------
/** @brief Get the number of triangles that are referenced by
* a vertex. This function returns a reference that can be modified
* @param iVertIndex Index of the vertex
* @return Number of referenced triangles */
unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) {
ai_assert( iVertIndex < mNumVertices );
ai_assert( nullptr != mLiveTriangles );
return mLiveTriangles[iVertIndex];
}
//! Offset table
unsigned int* mOffsetTable;
//! Adjacency table
unsigned int* mAdjacencyTable;
//! Table containing the number of referenced triangles per vertex
unsigned int* mLiveTriangles;
//! Debug: Number of referenced vertices
unsigned int mNumVertices;
};
} //! ns Assimp
#endif // !! AI_VTADJACENCY_H_INC

View File

@ -1,95 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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 Win32DebugLogStream.h
* @brief Implementation of Win32DebugLogStream
*/
#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC
#define AI_WIN32DEBUGLOGSTREAM_H_INC
#ifdef _WIN32
#include <assimp/LogStream.hpp>
#include "windows.h"
namespace Assimp {
// ---------------------------------------------------------------------------
/** @class Win32DebugLogStream
* @brief Logs into the debug stream from win32.
*/
class Win32DebugLogStream : public LogStream {
public:
/** @brief Default constructor */
Win32DebugLogStream();
/** @brief Destructor */
~Win32DebugLogStream();
/** @brief Writer */
void write(const char* messgae);
};
// ---------------------------------------------------------------------------
inline
Win32DebugLogStream::Win32DebugLogStream(){
// empty
}
// ---------------------------------------------------------------------------
inline
Win32DebugLogStream::~Win32DebugLogStream(){
// empty
}
// ---------------------------------------------------------------------------
inline
void Win32DebugLogStream::write(const char* message) {
::OutputDebugStringA( message);
}
// ---------------------------------------------------------------------------
} // Namespace Assimp
#endif // ! _WIN32
#endif // guard

View File

@ -1,196 +0,0 @@
#ifndef INCLUDED_ASSBIN_CHUNKS_H
#define INCLUDED_ASSBIN_CHUNKS_H
#define ASSBIN_VERSION_MAJOR 1
#define ASSBIN_VERSION_MINOR 0
/**
@page assfile .ASS File formats
@section over Overview
Assimp provides its own interchange format, which is intended to applications which need
to serialize 3D-models and to reload them quickly. Assimp's file formats are designed to
be read by Assimp itself. They encode additional information needed by Assimp to optimize
its postprocessing pipeline. If you once apply specific steps to a scene, then save it
and reread it from an ASS format using the same post processing settings, they won't
be executed again.
The format comes in two flavours: XML and binary - both of them hold a complete dump of
the 'aiScene' data structure returned by the APIs. The focus for the binary format
(<tt>.assbin</tt>) is fast loading. Optional deflate compression helps reduce file size. The XML
flavour, <tt>.assxml</tt> or simply .xml, is just a plain-to-xml conversion of aiScene.
ASSBIN is Assimp's binary interchange format. assimp_cmd (<tt>&lt;root&gt;/tools/assimp_cmd</tt>) is able to
write it and the core library provides a loader for it.
@section assxml XML File format
The format is pretty much self-explanatory due to its similarity to the in-memory aiScene structure.
With few exceptions, C structures are wrapped in XML elements.
The DTD for ASSXML can be found in <tt>&lt;root&gt;/doc/AssXML_Scheme.xml</tt>. Or have look
at the output files generated by assimp_cmd.
@section assbin Binary file format
The ASSBIN file format is composed of chunks to represent the hierarchical aiScene data structure.
This makes the format extensible and allows backward-compatibility with future data structure
versions. The <tt>&lt;root&gt;/code/assbin_chunks.h</tt> header contains some magic constants
for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found
in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDumb.cpp</tt> (yes, the 'b' is no typo ...).
@verbatim
-------------------------------------------------------------------------------
1. File structure:
-------------------------------------------------------------------------------
----------------------
| Header (512 bytes) |
----------------------
| Variable chunks |
----------------------
-------------------------------------------------------------------------------
2. Definitions:
-------------------------------------------------------------------------------
integer is four bytes wide, stored in little-endian byte order.
short is two bytes wide, stored in little-endian byte order.
byte is a single byte.
string is an integer n followed by n UTF-8 characters, not terminated by zero
float is an IEEE 754 single-precision floating-point value
double is an IEEE 754 double-precision floating-point value
t[n] is an array of n elements of type t
-------------------------------------------------------------------------------
2. Header:
-------------------------------------------------------------------------------
byte[44] Magic identification string for ASSBIN files.
'ASSIMP.binary'
integer Major version of the Assimp library which wrote the file
integer Minor version of the Assimp library which wrote the file
match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR
integer SVN revision of the Assimp library (intended for our internal
debugging - if you write Ass files from your own APPs, set this value to 0.
integer Assimp compile flags
short 0 for normal files, 1 for shortened dumps for regression tests
these should have the file extension assbin.regress
short 1 if the data after the header is compressed with the DEFLATE algorithm,
0 for uncompressed files.
For compressed files, the first integer after the header is
always the uncompressed data size
byte[256] Zero-terminated source file name, UTF-8
byte[128] Zero-terminated command line parameters passed to assimp_cmd, UTF-8
byte[64] Reserved for future use
---> Total length: 512 bytes
-------------------------------------------------------------------------------
3. Chunks:
-------------------------------------------------------------------------------
integer Magic chunk ID (ASSBIN_CHUNK_XXX)
integer Chunk data length, in bytes
(unknown chunks are possible, a good reader skips over them)
(chunk-data-length does not include the first two integers)
byte[n] chunk-data-length bytes of data, depending on the chunk type
Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk,
their size is included in chunk-data-length.
The chunk layout for all ASSIMP data structures is derived from their C declarations.
The general 'rule' to get from Assimp headers to the serialized layout is:
1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices),
in order of declaration.
2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights),
in order of declaration.
2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in
subchunks directly following the data written in 1.) and 2.)
Of course, there are some exceptions to this general order:
[[aiScene]]
- The root node holding the scene structure is naturally stored in
a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is
empty for aiScene).
[[aiMesh]]
- mTextureCoords and mNumUVComponents are serialized as follows:
[number of used uv channels times]
integer mNumUVComponents[n]
float mTextureCoords[n][3]
-> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp
builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange
data.
-> the on-disk format always uses 3 floats to write UV coordinates.
If mNumUVComponents[0] is 1, the corresponding mTextureCoords array
consists of 3 floats.
- The array member block of aiMesh is prefixed with an integer that specifies
the kinds of vertex components actually present in the mesh. This is a
bitwise combination of the ASSBIN_MESH_HAS_xxx constants.
[[aiFace]]
- mNumIndices is stored as short
- mIndices are written as short, if aiMesh::mNumVertices<65536
[[aiNode]]
- mParent is omitted
[[aiLight]]
- mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL
- mAngleXXX not written if aiLight::mType != aiLightSource_SPOT
[[aiMaterial]]
- mNumAllocated is omitted, for obvious reasons :-)
@endverbatim*/
#define ASSBIN_HEADER_LENGTH 512
// these are the magic chunk identifiers for the binary ASS file format
#define ASSBIN_CHUNK_AICAMERA 0x1234
#define ASSBIN_CHUNK_AILIGHT 0x1235
#define ASSBIN_CHUNK_AITEXTURE 0x1236
#define ASSBIN_CHUNK_AIMESH 0x1237
#define ASSBIN_CHUNK_AINODEANIM 0x1238
#define ASSBIN_CHUNK_AISCENE 0x1239
#define ASSBIN_CHUNK_AIBONE 0x123a
#define ASSBIN_CHUNK_AIANIMATION 0x123b
#define ASSBIN_CHUNK_AINODE 0x123c
#define ASSBIN_CHUNK_AIMATERIAL 0x123d
#define ASSBIN_CHUNK_AIMATERIALPROPERTY 0x123e
#define ASSBIN_MESH_HAS_POSITIONS 0x1
#define ASSBIN_MESH_HAS_NORMALS 0x2
#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS 0x4
#define ASSBIN_MESH_HAS_TEXCOORD_BASE 0x100
#define ASSBIN_MESH_HAS_COLOR_BASE 0x10000
#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n)
#define ASSBIN_MESH_HAS_COLOR(n) (ASSBIN_MESH_HAS_COLOR_BASE << n)
#endif // INCLUDED_ASSBIN_CHUNKS_H

View File

@ -1,140 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
---------------------------------------------------------------------------
*/
#include <assimp/scene.h>
aiNode::aiNode()
: mName("")
, mParent(nullptr)
, mNumChildren(0)
, mChildren(nullptr)
, mNumMeshes(0)
, mMeshes(nullptr)
, mMetaData(nullptr) {
// empty
}
aiNode::aiNode(const std::string& name)
: mName(name)
, mParent(nullptr)
, mNumChildren(0)
, mChildren(nullptr)
, mNumMeshes(0)
, mMeshes(nullptr)
, mMetaData(nullptr) {
// empty
}
/** Destructor */
aiNode::~aiNode() {
// delete all children recursively
// to make sure we won't crash if the data is invalid ...
if (mNumChildren && mChildren)
{
for (unsigned int a = 0; a < mNumChildren; a++)
delete mChildren[a];
}
delete[] mChildren;
delete[] mMeshes;
delete mMetaData;
}
const aiNode *aiNode::FindNode(const char* name) const {
if (nullptr == name) {
return nullptr;
}
if (!::strcmp(mName.data, name)) {
return this;
}
for (unsigned int i = 0; i < mNumChildren; ++i) {
const aiNode* const p = mChildren[i]->FindNode(name);
if (p) {
return p;
}
}
// there is definitely no sub-node with this name
return nullptr;
}
aiNode *aiNode::FindNode(const char* name) {
if (!::strcmp(mName.data, name))return this;
for (unsigned int i = 0; i < mNumChildren; ++i)
{
aiNode* const p = mChildren[i]->FindNode(name);
if (p) {
return p;
}
}
// there is definitely no sub-node with this name
return nullptr;
}
void aiNode::addChildren(unsigned int numChildren, aiNode **children) {
if (nullptr == children || 0 == numChildren) {
return;
}
for (unsigned int i = 0; i < numChildren; i++) {
aiNode *child = children[i];
if (nullptr != child) {
child->mParent = this;
}
}
if (mNumChildren > 0) {
aiNode **tmp = new aiNode*[mNumChildren];
::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren);
delete[] mChildren;
mChildren = new aiNode*[mNumChildren + numChildren];
::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren);
::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren);
mNumChildren += numChildren;
delete[] tmp;
}
else {
mChildren = new aiNode*[numChildren];
for (unsigned int i = 0; i < numChildren; i++) {
mChildren[i] = children[i];
}
mNumChildren = numChildren;
}
}

View File

@ -1,79 +0,0 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
---------------------------------------------------------------------------
*/
#include "simd.h"
namespace Assimp {
bool CPUSupportsSSE2() {
#if defined(__x86_64__) || defined(_M_X64)
//* x86_64 always has SSE2 instructions */
return true;
#elif defined(__GNUC__) && defined(i386)
// for GCC x86 we check cpuid
unsigned int d;
__asm__(
"pushl %%ebx\n\t"
"cpuid\n\t"
"popl %%ebx\n\t"
: "=d" ( d )
:"a" ( 1 ) );
return ( d & 0x04000000 ) != 0;
#elif (defined(_MSC_VER) && defined(_M_IX86))
// also check cpuid for MSVC x86
unsigned int d;
__asm {
xor eax, eax
inc eax
push ebx
cpuid
pop ebx
mov d, edx
}
return ( d & 0x04000000 ) != 0;
#else
return false;
#endif
}
} // Namespace Assimp

Some files were not shown because too many files have changed in this diff Show More