Merge pull request #23837 from fire/asset_import

Add Open Asset Importer to Godot.
This commit is contained in:
Rémi Verschelde 2019-04-06 00:12:46 +02:00 committed by GitHub
commit 7ca9863079
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
246 changed files with 74970 additions and 2 deletions

View File

@ -110,6 +110,11 @@ Copyright: 2007, Starbreeze Studios
2014-2019, 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/b2d_convexdecomp/
Comment: Box2D (ConvexDecomp)
Copyright: 2007, Eric Jordan

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorSceneImporterAssimp" inherits="EditorSceneImporter" category="Core" version="3.2">
<brief_description>
This is a multi-format 3d asset importer.
</brief_description>
<description>
This is a multi-format 3d asset importer.
Use these FBX export settings from Autodesk Maya.
[codeblock]
* Smoothing Groups
* Smooth Mesh
* Triangluate (For mesh with blendshapes)
* Bake Animation
* Resample All
* Deformed Models
* Skins
* Blend Shapes
* Curve Filters
* Constant Key Reducer
* Auto Tangents Only
* DO NOT CHECK Constraints (Will Break File)
* Can check Embed Media (Embeds textures into FBX file to import)
-- Note: When importing embed media, texture and mesh will be a un-alterable file.
-- Reimport of fbx with updated texture is need if texture is updated.
* Units: Centimeters
* Up Axis: Y
* Binary format in FBX 2017
[/codeblock]
</description>
<tutorials>
</tutorials>
<demos>
</demos>
<methods>
</methods>
<constants>
</constants>
</class>

View File

@ -4131,7 +4131,7 @@ void RasterizerStorageGLES3::mesh_render_blend_shapes(Surface *s, const float *p
for (int ti = 0; ti < mtc; ti++) {
float weight = p_weights[ti];
if (weight < 0.001) //not bother with this one
if (weight < 0.00001) //not bother with this one
continue;
glBindVertexArray(s->blend_shapes[ti].array_id);

95
modules/assimp/SCsub Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
Import('env')
Import('env_modules')
env_assimp = env_modules.Clone()
env_assimp.Append(CPPPATH=['#thirdparty/assimp'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/include'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/code/Importer/IFC'])
env_assimp.Append(CPPPATH=['#thirdparty/misc'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/code'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/irrXML/'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/unzip/'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/code/Importer/STEPParser'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/'])
env_assimp.Append(CPPPATH=['#thirdparty/zlib/'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/openddlparser/include'])
env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/rapidjson/include'])
env_assimp.Append(CPPPATH=['.'])
#env_assimp.Append(CPPFLAGS=['-DASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_BOOST_WORKAROUND'])
env_assimp.Append(CPPFLAGS=['-DOPENDDLPARSER_BUILD'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OWN_ZLIB'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_EXPORT'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_X_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_AMF_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_3DS_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD3_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD5_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MDL_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD2_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_PLY_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_ASE_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OBJ_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_HMP_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_SMD_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MDC_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MD5_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_STL_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_LWO_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_DXF_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_NFF_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_RAW_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_SIB_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OFF_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_AC_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_BVH_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_IRRMESH_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_IRR_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_Q3D_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_B3D_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_COLLADA_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_TERRAGEN_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_CSM_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_3D_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_LWS_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OGRE_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_OPENGEX_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_MS3D_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_COB_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_BLEND_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_Q3BSP_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_NDO_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_STEP_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_IFC_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_XGL_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_ASSBIN_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_GLTF_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_C4D_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_3MF_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_NO_X3D_IMPORTER'])
env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_SINGLETHREADED'])
if (not env.msvc):
env_assimp.Append(CXXFLAGS=['-std=c++11'])
elif (env.msvc == False and env['platform'] == 'windows'):
env_assimp.Append(LDFLAGS=['-pthread'])
if(env['platform'] == 'windows'):
env_assimp.Append(CPPFLAGS=['-DPLATFORM_WINDOWS'])
env_assimp.Append(CPPFLAGS=['-DPLATFORM=WINDOWS'])
elif(env['platform'] == 'x11'):
env_assimp.Append(CPPFLAGS=['-DPLATFORM_LINUX'])
env_assimp.Append(CPPFLAGS=['-DPLATFORM=LINUX'])
elif(env['platform'] == 'osx'):
env_assimp.Append(CPPFLAGS=['-DPLATFORM_DARWIN'])
env_assimp.Append(CPPFLAGS=['-DPLATFORM=DARWIN'])
env_thirdparty = env_assimp.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, Glob('#thirdparty/assimp/code/*.cpp'))
# Godot's own source files
env_assimp.add_source_files(env.modules_sources, "*.cpp")

5
modules/assimp/config.py Normal file
View File

@ -0,0 +1,5 @@
def can_build(env, platform):
return env['tools']
def configure(env):
pass

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
/*************************************************************************/
/* editor_scene_importer_assimp.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 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 EDITOR_SCENE_IMPORTER_ASSIMP_H
#define EDITOR_SCENE_IMPORTER_ASSIMP_H
#ifdef TOOLS_ENABLED
#include "core/bind/core_bind.h"
#include "core/io/resource_importer.h"
#include "core/vector.h"
#include "editor/import/resource_importer_scene.h"
#include "editor/project_settings_editor.h"
#include "scene/3d/mesh_instance.h"
#include "scene/3d/skeleton.h"
#include "scene/3d/spatial.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/animation.h"
#include "scene/resources/surface_tool.h"
#include "assimp/DefaultLogger.hpp"
#include "assimp/LogStream.hpp"
#include "assimp/Logger.hpp"
#include "assimp/matrix4x4.h"
#include "assimp/scene.h"
#include "assimp/types.h"
class AssimpStream : public Assimp::LogStream {
public:
// Constructor
AssimpStream();
// Destructor
~AssimpStream();
// Write something using your own functionality
void write(const char *message);
};
#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor", 0, 0
#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness", 0, 0
#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness", 0, 0
#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling", 0, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color", 0, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive", 0, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic", 0, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness", 0, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity", 0, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file", aiTextureType_UNKNOWN, 0
#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo", aiTextureType_UNKNOWN, 0
class EditorSceneImporterAssimp : public EditorSceneImporter {
private:
GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
const String ASSIMP_FBX_KEY = "_$AssimpFbx$";
struct AssetImportAnimation {
enum Interpolation {
INTERP_LINEAR,
INTERP_STEP,
INTERP_CATMULLROMSPLINE,
INTERP_CUBIC_SPLINE
};
};
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
};
};
Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
void _fill_kept_node(Set<Node *> &keep_nodes);
String _find_skeleton_bone_root(Map<Skeleton *, MeshInstance *> &skeletons, Map<MeshInstance *, String> &meshes, Spatial *root);
void _set_bone_parent(Skeleton *s, Node *p_owner, aiNode *p_node);
Transform _get_global_ai_node_transform(const aiScene *p_scene, const aiNode *p_current_node);
void _generate_node_bone(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const String p_path, const int32_t p_max_bone_weights);
void _generate_node_bone_parents(const aiScene *p_scene, const aiNode *p_node, Map<String, bool> &p_mesh_bones, Skeleton *p_skeleton, const MeshInstance *p_mi);
void _calculate_skeleton_root(Skeleton *s, const aiScene *p_scene, aiNode *&p_ai_skeleton_root, Map<String, bool> &mesh_bones, const aiNode *p_node);
void _fill_skeleton(const aiScene *p_scene, const aiNode *p_node, Spatial *p_current, Node *p_owner, Skeleton *p_skeleton, const Map<String, bool> p_mesh_bones, const Map<String, Transform> &p_bone_rests, Set<String> p_tracks, const String p_path, Set<String> &r_removed_bones);
void _keep_node(const String &p_path, Node *p_current, Node *p_owner, Set<Node *> &r_keep_nodes);
void _filter_node(const String &p_path, Node *p_current, Node *p_owner, const Set<Node *> p_keep_nodes, Set<String> &r_removed_nodes);
void _generate_node(const String &p_path, const aiScene *p_scene, const aiNode *p_node, Node *p_parent, Node *p_owner, Set<String> &r_bone_name, Set<String> p_light_names, Set<String> p_camera_names, Map<Skeleton *, MeshInstance *> &r_skeletons, const Map<String, Transform> &p_bone_rests, Vector<MeshInstance *> &r_mesh_instances, int32_t &r_mesh_count, Skeleton *p_skeleton, const int32_t p_max_bone_weights, Set<String> &r_removed_bones, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names);
aiNode *_ai_find_node(aiNode *ai_child_node, const String bone_name);
Transform _format_rot_xform(const String p_path, const aiScene *p_scene);
void _get_track_set(const aiScene *p_scene, Set<String> &tracks);
void _insert_animation_track(const aiScene *p_scene, const String p_path, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, float length, const Skeleton *sk, const aiNodeAnim *track, String node_name, NodePath node_path);
void _add_mesh_to_mesh_instance(const aiNode *p_node, const aiScene *p_scene, Skeleton *s, const String &p_path, MeshInstance *p_mesh_instance, Node *p_owner, Set<String> &r_bone_name, int32_t &r_mesh_count, int32_t p_max_bone_weights, Map<String, Map<uint32_t, String> > &r_name_morph_mesh_names);
Ref<Texture> _load_texture(const aiScene *p_scene, String p_path);
void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w);
void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture);
void _find_texture_path(const String &p_path, String &path, bool &r_found);
void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension);
String _ai_string_to_string(const aiString p_string) const;
String _ai_anim_string_to_string(const aiString p_string) const;
String _ai_raw_string_to_string(const aiString p_string) const;
void _import_animation(const String p_path, const Vector<MeshInstance *> p_meshes, const aiScene *p_scene, AnimationPlayer *ap, int32_t p_index, int p_bake_fps, Map<Skeleton *, MeshInstance *> p_skeletons, const Set<String> p_removed_nodes, const Set<String> removed_bones, const Map<String, Map<uint32_t, String> > p_path_morph_mesh_names);
void _insert_pivot_anim_track(const Vector<MeshInstance *> p_meshes, const String p_node_name, Vector<const aiNodeAnim *> F, AnimationPlayer *ap, Skeleton *sk, float &length, float ticks_per_second, Ref<Animation> animation, int p_bake_fps, const String &p_path, const aiScene *p_scene);
float _get_fbx_fps(int32_t time_mode, const aiScene *p_scene);
template <class T>
T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
const Transform _ai_matrix_transform(const aiMatrix4x4 p_matrix);
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();
}
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);
virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps);
};
#endif
#endif

View File

@ -0,0 +1,261 @@
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

@ -0,0 +1,53 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 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 "register_types.h"
#include "editor/editor_node.h"
#include "editor_scene_importer_assimp.h"
#ifdef TOOLS_ENABLED
static void _editor_init() {
Ref<EditorSceneImporterAssimp> import_assimp;
import_assimp.instance();
ResourceImporterScene::get_singleton()->add_importer(import_assimp);
}
#endif
void register_assimp_types() {
#ifdef TOOLS_ENABLED
ClassDB::register_class<EditorSceneImporterAssimp>();
EditorNode::add_init_callback(_editor_init);
#endif
}
void unregister_assimp_types() {
}

View File

@ -0,0 +1,32 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 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. */
/*************************************************************************/
void register_assimp_types();
void unregister_assimp_types();

View File

@ -96,7 +96,7 @@ void MeshInstance::_get_property_list(List<PropertyInfo> *p_list) const {
ls.sort();
for (List<String>::Element *E = ls.front(); E; E = E->next()) {
p_list->push_back(PropertyInfo(Variant::REAL, E->get(), PROPERTY_HINT_RANGE, "0,1,0.01"));
p_list->push_back(PropertyInfo(Variant::REAL, E->get(), PROPERTY_HINT_RANGE, "0,1,0.00001"));
}
if (mesh.is_valid()) {

View File

@ -1,5 +1,11 @@
# Third party libraries
## assimp
- Upstream: http://github.com/assimp/assimp
- Version: git (d2b45377e4b09a1f43be95e45553afcc06b03f4b)
- License: BSD-3-Clause
## b2d_convexdecomp

183
thirdparty/assimp/CREDITS vendored Normal file
View File

@ -0,0 +1,183 @@
===============================================================
Open Asset Import Library (Assimp)
Developers and Contributors
===============================================================
The following is a non-exhaustive list of all constributors over the years.
If you think your name should be listed here, drop us a line and we'll add you.
- Alexander Gessler,
3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design).
- Thomas Schulze,
X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation.
- Kim Kulling,
Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ).
- R.Schmidt,
Linux build, eclipse support.
- Matthias Gubisch,
Assimp.net
Visual Studio 9 support, bugfixes.
- Mark Sibly
B3D-Loader, Assimp testing
- Jonathan Klein
Ogre Loader, VC2010 fixes and CMake fixes.
- Sebastian Hempel,
PyAssimp (first version)
Compile-Bugfixes for mingw, add environment for static library support in make.
- Jonathan Pokrass
Supplied a bugfix concerning the scaling in the md3 loader.
- Andrew Galante,
Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
- Andreas Nagel
First Assimp testing & verification under Windows Vista 64 Bit.
- Marius Schr<68>der
Allowed us to use many of his models for screenshots and testing.
- Christian Schubert
Supplied various XFiles for testing purposes.
- Tizian Wieland
Searched the web for hundreds of test models for internal use
- John Connors
Supplied patches for linux and SCons.
- T. R.
The GUY who performed some of the CSM mocaps.
- Andy Maloney
Contributed fixes for the documentation and the doxygen markup
- Zhao Lei
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.
- Tobias Rittig
Collada testing with Cinema 4D
- Brad Grantham
Improvements in OpenGL-Sample.
- Robert Ramirez
Add group loading feature to Obj-Loader.
- Chris Maiwald
Many bugreports, improving Assimp's portability, regular testing & feedback.
- Stepan Hrbek
Bugreport and fix for a obj-materialloader crash.
- David Nadlinger
D bindings, CMake install support.
- Dario Accornero
Contributed several patches regarding Mac OS/XCode targets, bug reports.
- Martin Walser (Samhayne)
Contributed the 'SimpleTexturedOpenGl' sample.
- Matthias Fauconneau
Contributed a fix for the Q3-BSP loader.
- Jørgen P. Tjernø
Contributed updated and improved xcode workspaces
- drparallax
Contributed the /samples/SimpleAssimpViewX sample
- Carsten Fuchs
Contributed a fix for the Normalize method in aiQuaternion.
- dbburgess
Contributes a Android-specific build issue: log the hardware architecture for ARM.
- alfiereinre7
Contributes a obj-fileparser fix: missing tokens in the obj-token list.
- Roman Kharitonov
Contributes a fix for the configure script environment.
- Ed Diana
Contributed AssimpDelphi (/port/AssimpDelphi).
- rdb
Contributes a bundle of fixes and improvements for the bsp-importer.
- Mick P
For contributing the De-bone postprocessing step and filing various bug reports.
- Rosen Diankov
Contributed patches to build assimp debian packages using cmake.
- Mark Page
Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
- IOhannes
Contributed the Debian build fixes ( architecture macro ).
- gellule
Several LWO and LWS fixes (pivoting).
- Marcel Metz
GCC/Linux fixes for the SimpleOpenGL sample.
- Brian Miller
Bugfix for a compiler fix for iOS on arm.
- Séverin Lemaignan
Rewrite of PyAssimp, distutils and Python3 support
- albert-wang
Bugfixes for the collada parser
- Ya ping Jin
Bugfixes for uv-tanget calculation.
- Jonne Nauha
Ogre Binary format support
- Filip Wasil, Tieto Poland Sp. z o.o.
Android JNI asset extraction support
- Richard Steffen
Contributed ExportProperties interface
Contributed X File exporter
Contributed Step (stp) exporter
- Thomas Iorns (mesilliac)
Initial FBX Export support
For a more detailed list just check: https://github.com/assimp/assimp/network/members
========
Patreons
========
Huge thanks to our Patreons!
- migenius
- Marcus
- Cort
- elect
- Steffen
===================
Commercial Sponsors
===================
- MyDidimo (mydidimo.com): Sponsored development of FBX Export support

78
thirdparty/assimp/LICENSE vendored Normal file
View File

@ -0,0 +1,78 @@
Open Asset Import Library (assimp)
Copyright (c) 2006-2016, 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.
******************************************************************************
AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
These are 3d models for testing purposes, from various free sources
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
if you don't want your files to appear in the repository.
******************************************************************************
Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
http://code.google.com/p/poly2tri/
All rights reserved.
Redistribution and use 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 Poly2Tri nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
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.

980
thirdparty/assimp/assimp/config.h vendored Normal file
View File

@ -0,0 +1,980 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2018, 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 config.h
* @brief Defines constants for configurable properties for the library
*
* Typically these properties are set via
* #Assimp::Importer::SetPropertyFloat,
* #Assimp::Importer::SetPropertyInteger or
* #Assimp::Importer::SetPropertyString,
* depending on the data type of a property. All properties have a
* default value. See the doc for the mentioned methods for more details.
*
* <br><br>
* The corresponding functions for use with the plain-c API are:
* #aiSetImportPropertyInteger,
* #aiSetImportPropertyFloat,
* #aiSetImportPropertyString
*/
#pragma once
#ifndef AI_CONFIG_H_INC
#define AI_CONFIG_H_INC
// ###########################################################################
// LIBRARY SETTINGS
// General, global settings
// ###########################################################################
// ---------------------------------------------------------------------------
/** @brief Enables time measurements.
*
* If enabled, measures the time needed for each part of the loading
* process (i.e. IO time, importing, postprocessing, ..) and dumps
* these timings to the DefaultLogger. See the @link perf Performance
* Page@endlink for more information on this topic.
*
* Property type: bool. Default value: false.
*/
#define AI_CONFIG_GLOB_MEASURE_TIME \
"GLOB_MEASURE_TIME"
// ---------------------------------------------------------------------------
/** @brief Global setting to disable generation of skeleton dummy meshes
*
* Skeleton dummy meshes are generated as a visualization aid in cases which
* the input data contains no geometry, but only animation data.
* Property data type: bool. Default value: false
*/
// ---------------------------------------------------------------------------
#define AI_CONFIG_IMPORT_NO_SKELETON_MESHES \
"IMPORT_NO_SKELETON_MESHES"
#if 0 // not implemented yet
// ---------------------------------------------------------------------------
/** @brief Set Assimp's multithreading policy.
*
* This setting is ignored if Assimp was built without boost.thread
* support (ASSIMP_BUILD_NO_THREADING, which is implied by ASSIMP_BUILD_BOOST_WORKAROUND).
* Possible values are: -1 to let Assimp decide what to do, 0 to disable
* multithreading entirely and any number larger than 0 to force a specific
* number of threads. Assimp is always free to ignore this settings, which is
* merely a hint. Usually, the default value (-1) will be fine. However, if
* Assimp is used concurrently from multiple user threads, it might be useful
* to limit each Importer instance to a specific number of cores.
*
* For more information, see the @link threading Threading page@endlink.
* Property type: int, default value: -1.
*/
#define AI_CONFIG_GLOB_MULTITHREADING \
"GLOB_MULTITHREADING"
#endif
// ###########################################################################
// POST PROCESSING SETTINGS
// Various stuff to fine-tune the behavior of a specific post processing step.
// ###########################################################################
// ---------------------------------------------------------------------------
/** @brief Maximum bone count per mesh for the SplitbyBoneCount step.
*
* Meshes are split until the maximum number of bones is reached. The default
* value is AI_SBBC_DEFAULT_MAX_BONES, which may be altered at
* compile-time.
* Property data type: integer.
*/
// ---------------------------------------------------------------------------
#define AI_CONFIG_PP_SBBC_MAX_BONES \
"PP_SBBC_MAX_BONES"
// default limit for bone count
#if (!defined AI_SBBC_DEFAULT_MAX_BONES)
#define AI_SBBC_DEFAULT_MAX_BONES 60
#endif
// ---------------------------------------------------------------------------
/** @brief Specifies the maximum angle that may be between two vertex tangents
* that their tangents and bi-tangents are smoothed.
*
* This applies to the CalcTangentSpace-Step. The angle is specified
* in degrees. The maximum value is 175.
* Property type: float. Default value: 45 degrees
*/
#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE \
"PP_CT_MAX_SMOOTHING_ANGLE"
// ---------------------------------------------------------------------------
/** @brief Source UV channel for tangent space computation.
*
* The specified channel must exist or an error will be raised.
* Property type: integer. Default value: 0
*/
// ---------------------------------------------------------------------------
#define AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX \
"PP_CT_TEXTURE_CHANNEL_INDEX"
// ---------------------------------------------------------------------------
/** @brief Specifies the maximum angle that may be between two face normals
* at the same vertex position that their are smoothed together.
*
* Sometimes referred to as 'crease angle'.
* This applies to the GenSmoothNormals-Step. The angle is specified
* in degrees, so 180 is PI. The default value is 175 degrees (all vertex
* normals are smoothed). The maximum value is 175, too. Property type: float.
* Warning: setting this option may cause a severe loss of performance. The
* performance is unaffected if the #AI_CONFIG_FAVOUR_SPEED flag is set but
* the output quality may be reduced.
*/
#define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE \
"PP_GSN_MAX_SMOOTHING_ANGLE"
// ---------------------------------------------------------------------------
/** @brief Sets the colormap (= palette) to be used to decode embedded
* textures in MDL (Quake or 3DGS) files.
*
* This must be a valid path to a file. The file is 768 (256*3) bytes
* large and contains RGB triplets for each of the 256 palette entries.
* The default value is colormap.lmp. If the file is not found,
* a default palette (from Quake 1) is used.
* Property type: string.
*/
#define AI_CONFIG_IMPORT_MDL_COLORMAP \
"IMPORT_MDL_COLORMAP"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_RemoveRedundantMaterials step to
* keep materials matching a name in a given list.
*
* This is a list of 1 to n strings, ' ' serves as delimiter character.
* Identifiers containing whitespaces must be enclosed in *single*
* quotation marks. For example:<tt>
* "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'"</tt>.
* If a material matches on of these names, it will not be modified or
* removed by the postprocessing step nor will other materials be replaced
* by a reference to it. <br>
* This option might be useful if you are using some magic material names
* to pass additional semantics through the content pipeline. This ensures
* they won't be optimized away, but a general optimization is still
* performed for materials not contained in the list.
* Property type: String. Default value: n/a
* @note Linefeeds, tabs or carriage returns are treated as whitespace.
* Material names are case sensitive.
*/
#define AI_CONFIG_PP_RRM_EXCLUDE_LIST \
"PP_RRM_EXCLUDE_LIST"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_PreTransformVertices step to
* keep the scene hierarchy. Meshes are moved to worldspace, but
* no optimization is performed (read: meshes with equal materials are not
* joined. The total number of meshes won't change).
*
* This option could be of use for you if the scene hierarchy contains
* important additional information which you intend to parse.
* For rendering, you can still render all meshes in the scene without
* any transformations.
* Property type: bool. Default value: false.
*/
#define AI_CONFIG_PP_PTV_KEEP_HIERARCHY \
"PP_PTV_KEEP_HIERARCHY"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_PreTransformVertices step to normalize
* all vertex components into the [-1,1] range. That is, a bounding box
* for the whole scene is computed, the maximum component is taken and all
* meshes are scaled appropriately (uniformly of course!).
* This might be useful if you don't know the spatial dimension of the input
* data*/
#define AI_CONFIG_PP_PTV_NORMALIZE \
"PP_PTV_NORMALIZE"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_PreTransformVertices step to use
* a users defined matrix as the scene root node transformation before
* transforming vertices.
* Property type: bool. Default value: false.
*/
#define AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION \
"PP_PTV_ADD_ROOT_TRANSFORMATION"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_PreTransformVertices step to use
* a users defined matrix as the scene root node transformation before
* transforming vertices. This property correspond to the 'a1' component
* of the transformation matrix.
* Property type: aiMatrix4x4.
*/
#define AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION \
"PP_PTV_ROOT_TRANSFORMATION"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_FindDegenerates step to
* remove degenerated primitives from the import - immediately.
*
* The default behaviour converts degenerated triangles to lines and
* degenerated lines to points. See the documentation to the
* #aiProcess_FindDegenerates step for a detailed example of the various ways
* to get rid of these lines and points if you don't want them.
* Property type: bool. Default value: false.
*/
#define AI_CONFIG_PP_FD_REMOVE \
"PP_FD_REMOVE"
// ---------------------------------------------------------------------------
/**
* @brief Configures the #aiProcess_FindDegenerates to check the area of a
* trinagle to be greates than e-6. If this is not the case the triangle will
* be removed if #AI_CONFIG_PP_FD_REMOVE is set to true.
*/
#define AI_CONFIG_PP_FD_CHECKAREA \
"PP_FD_CHECKAREA"
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_OptimizeGraph step to preserve nodes
* matching a name in a given list.
*
* This is a list of 1 to n strings, ' ' serves as delimiter character.
* Identifiers containing whitespaces must be enclosed in *single*
* quotation marks. For example:<tt>
* "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'"</tt>.
* If a node matches on of these names, it will not be modified or
* removed by the postprocessing step.<br>
* This option might be useful if you are using some magic node names
* to pass additional semantics through the content pipeline. This ensures
* they won't be optimized away, but a general optimization is still
* performed for nodes not contained in the list.
* Property type: String. Default value: n/a
* @note Linefeeds, tabs or carriage returns are treated as whitespace.
* Node names are case sensitive.
*/
#define AI_CONFIG_PP_OG_EXCLUDE_LIST \
"PP_OG_EXCLUDE_LIST"
// ---------------------------------------------------------------------------
/** @brief Set the maximum number of triangles in a mesh.
*
* This is used by the "SplitLargeMeshes" PostProcess-Step to determine
* whether a mesh must be split or not.
* @note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES
* Property type: integer.
*/
#define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT \
"PP_SLM_TRIANGLE_LIMIT"
// default value for AI_CONFIG_PP_SLM_TRIANGLE_LIMIT
#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES)
#define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000
#endif
// ---------------------------------------------------------------------------
/** @brief Set the maximum number of vertices in a mesh.
*
* This is used by the "SplitLargeMeshes" PostProcess-Step to determine
* whether a mesh must be split or not.
* @note The default value is AI_SLM_DEFAULT_MAX_VERTICES
* Property type: integer.
*/
#define AI_CONFIG_PP_SLM_VERTEX_LIMIT \
"PP_SLM_VERTEX_LIMIT"
// default value for AI_CONFIG_PP_SLM_VERTEX_LIMIT
#if (!defined AI_SLM_DEFAULT_MAX_VERTICES)
#define AI_SLM_DEFAULT_MAX_VERTICES 1000000
#endif
// ---------------------------------------------------------------------------
/** @brief Set the maximum number of bones affecting a single vertex
*
* This is used by the #aiProcess_LimitBoneWeights PostProcess-Step.
* @note The default value is AI_LMW_MAX_WEIGHTS
* Property type: integer.*/
#define AI_CONFIG_PP_LBW_MAX_WEIGHTS \
"PP_LBW_MAX_WEIGHTS"
// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
#if (!defined AI_LMW_MAX_WEIGHTS)
#define AI_LMW_MAX_WEIGHTS 0x4
#endif // !! AI_LMW_MAX_WEIGHTS
// ---------------------------------------------------------------------------
/** @brief Lower the deboning threshold in order to remove more bones.
*
* This is used by the #aiProcess_Debone PostProcess-Step.
* @note The default value is AI_DEBONE_THRESHOLD
* Property type: float.*/
#define AI_CONFIG_PP_DB_THRESHOLD \
"PP_DB_THRESHOLD"
// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
#if (!defined AI_DEBONE_THRESHOLD)
#define AI_DEBONE_THRESHOLD 1.0f
#endif // !! AI_DEBONE_THRESHOLD
// ---------------------------------------------------------------------------
/** @brief Require all bones qualify for deboning before removing any
*
* This is used by the #aiProcess_Debone PostProcess-Step.
* @note The default value is 0
* Property type: bool.*/
#define AI_CONFIG_PP_DB_ALL_OR_NONE \
"PP_DB_ALL_OR_NONE"
/** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property
*/
#ifndef PP_ICL_PTCACHE_SIZE
#define PP_ICL_PTCACHE_SIZE 12
#endif
// ---------------------------------------------------------------------------
/** @brief Set the size of the post-transform vertex cache to optimize the
* vertices for. This configures the #aiProcess_ImproveCacheLocality step.
*
* The size is given in vertices. Of course you can't know how the vertex
* format will exactly look like after the import returns, but you can still
* guess what your meshes will probably have.
* @note The default value is #PP_ICL_PTCACHE_SIZE. That results in slight
* performance improvements for most nVidia/AMD cards since 2002.
* Property type: integer.
*/
#define AI_CONFIG_PP_ICL_PTCACHE_SIZE "PP_ICL_PTCACHE_SIZE"
// ---------------------------------------------------------------------------
/** @brief Enumerates components of the aiScene and aiMesh data structures
* that can be excluded from the import using the #aiProcess_RemoveComponent step.
*
* See the documentation to #aiProcess_RemoveComponent for more details.
*/
enum aiComponent {
/** Normal vectors */
#ifdef SWIG
aiComponent_NORMALS = 0x2,
#else
aiComponent_NORMALS = 0x2u,
#endif
/** Tangents and bitangents go always together ... */
#ifdef SWIG
aiComponent_TANGENTS_AND_BITANGENTS = 0x4,
#else
aiComponent_TANGENTS_AND_BITANGENTS = 0x4u,
#endif
/** ALL color sets
* Use aiComponent_COLORn(N) to specify the N'th set */
aiComponent_COLORS = 0x8,
/** ALL texture UV sets
* aiComponent_TEXCOORDn(N) to specify the N'th set */
aiComponent_TEXCOORDS = 0x10,
/** Removes all bone weights from all meshes.
* The scenegraph nodes corresponding to the bones are NOT removed.
* use the #aiProcess_OptimizeGraph step to do this */
aiComponent_BONEWEIGHTS = 0x20,
/** Removes all node animations (aiScene::mAnimations).
* The corresponding scenegraph nodes are NOT removed.
* use the #aiProcess_OptimizeGraph step to do this */
aiComponent_ANIMATIONS = 0x40,
/** Removes all embedded textures (aiScene::mTextures) */
aiComponent_TEXTURES = 0x80,
/** Removes all light sources (aiScene::mLights).
* The corresponding scenegraph nodes are NOT removed.
* use the #aiProcess_OptimizeGraph step to do this */
aiComponent_LIGHTS = 0x100,
/** Removes all cameras (aiScene::mCameras).
* The corresponding scenegraph nodes are NOT removed.
* use the #aiProcess_OptimizeGraph step to do this */
aiComponent_CAMERAS = 0x200,
/** Removes all meshes (aiScene::mMeshes). */
aiComponent_MESHES = 0x400,
/** Removes all materials. One default material will
* be generated, so aiScene::mNumMaterials will be 1. */
aiComponent_MATERIALS = 0x800,
/** This value is not used. It is just there to force the
* compiler to map this enum to a 32 Bit integer. */
#ifndef SWIG
_aiComponent_Force32Bit = 0x9fffffff
#endif
};
// Remove a specific color channel 'n'
#define aiComponent_COLORSn(n) (1u << (n + 20u))
// Remove a specific UV channel 'n'
#define aiComponent_TEXCOORDSn(n) (1u << (n + 25u))
// ---------------------------------------------------------------------------
/** @brief Input parameter to the #aiProcess_RemoveComponent step:
* Specifies the parts of the data structure to be removed.
*
* See the documentation to this step for further details. The property
* is expected to be an integer, a bitwise combination of the
* #aiComponent flags defined above in this header. The default
* value is 0. Important: if no valid mesh is remaining after the
* step has been executed (e.g you thought it was funny to specify ALL
* of the flags defined above) the import FAILS. Mainly because there is
* no data to work on anymore ...
*/
#define AI_CONFIG_PP_RVC_FLAGS \
"PP_RVC_FLAGS"
// ---------------------------------------------------------------------------
/** @brief Input parameter to the #aiProcess_SortByPType step:
* Specifies which primitive types are removed by the step.
*
* This is a bitwise combination of the aiPrimitiveType flags.
* Specifying all of them is illegal, of course. A typical use would
* be to exclude all line and point meshes from the import. This
* is an integer property, its default value is 0.
*/
#define AI_CONFIG_PP_SBP_REMOVE \
"PP_SBP_REMOVE"
// ---------------------------------------------------------------------------
/** @brief Input parameter to the #aiProcess_FindInvalidData step:
* Specifies the floating-point accuracy for animation values. The step
* checks for animation tracks where all frame values are absolutely equal
* and removes them. This tweakable controls the epsilon for floating-point
* comparisons - two keys are considered equal if the invariant
* abs(n0-n1)>epsilon holds true for all vector respectively quaternion
* components. The default value is 0.f - comparisons are exact then.
*/
#define AI_CONFIG_PP_FID_ANIM_ACCURACY \
"PP_FID_ANIM_ACCURACY"
// TransformUVCoords evaluates UV scalings
#define AI_UVTRAFO_SCALING 0x1
// TransformUVCoords evaluates UV rotations
#define AI_UVTRAFO_ROTATION 0x2
// TransformUVCoords evaluates UV translation
#define AI_UVTRAFO_TRANSLATION 0x4
// Everything baked together -> default value
#define AI_UVTRAFO_ALL (AI_UVTRAFO_SCALING | AI_UVTRAFO_ROTATION | AI_UVTRAFO_TRANSLATION)
// ---------------------------------------------------------------------------
/** @brief Input parameter to the #aiProcess_FindInvalidData step:
* Set to true to ignore texture coordinates. This may be useful if you have
* to assign different kind of textures like one for the summer or one for the winter.
*/
#define AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS \
"PP_FID_IGNORE_TEXTURECOORDS"
// ---------------------------------------------------------------------------
/** @brief Input parameter to the #aiProcess_TransformUVCoords step:
* Specifies which UV transformations are evaluated.
*
* This is a bitwise combination of the AI_UVTRAFO_XXX flags (integer
* property, of course). By default all transformations are enabled
* (AI_UVTRAFO_ALL).
*/
#define AI_CONFIG_PP_TUV_EVALUATE \
"PP_TUV_EVALUATE"
// ---------------------------------------------------------------------------
/** @brief A hint to assimp to favour speed against import quality.
*
* Enabling this option may result in faster loading, but it needn't.
* It represents just a hint to loaders and post-processing steps to use
* faster code paths, if possible.
* This property is expected to be an integer, != 0 stands for true.
* The default value is 0.
*/
#define AI_CONFIG_FAVOUR_SPEED \
"FAVOUR_SPEED"
// ###########################################################################
// IMPORTER SETTINGS
// Various stuff to fine-tune the behaviour of specific importer plugins.
// ###########################################################################
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will merge all geometry layers present
* in the source file or take only the first.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS \
"IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will read all materials present in the
* source file or take only the referenced materials.
*
* This is void unless IMPORT_FBX_READ_MATERIALS=1.
*
* The default value is false (0)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS \
"IMPORT_FBX_READ_ALL_MATERIALS"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will read materials.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_MATERIALS \
"IMPORT_FBX_READ_MATERIALS"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will read embedded textures.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_TEXTURES \
"IMPORT_FBX_READ_TEXTURES"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will read cameras.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_CAMERAS \
"IMPORT_FBX_READ_CAMERAS"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will read light sources.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_LIGHTS \
"IMPORT_FBX_READ_LIGHTS"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will read animations.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS \
"IMPORT_FBX_READ_ANIMATIONS"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will act in strict mode in which only
* FBX 2013 is supported and any other sub formats are rejected. FBX 2013
* is the primary target for the importer, so this format is best
* supported and well-tested.
*
* The default value is false (0)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_STRICT_MODE \
"IMPORT_FBX_STRICT_MODE"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will preserve pivot points for
* transformations (as extra nodes). If set to false, pivots and offsets
* will be evaluated whenever possible.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS \
"IMPORT_FBX_PRESERVE_PIVOTS"
// ---------------------------------------------------------------------------
/** @brief Specifies whether the importer will drop empty animation curves or
* animation curves which match the bind pose transformation over their
* entire defined range.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES \
"IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES"
// ---------------------------------------------------------------------------
/** @brief Set whether the fbx importer will use the legacy embedded texture naming.
*
* The default value is false (0)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \
"AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
// ---------------------------------------------------------------------------
/** @brief Set the vertex animation keyframe to be imported
*
* ASSIMP does not support vertex keyframes (only bone animation is supported).
* The library reads only one frame of models with vertex animations.
* By default this is the first frame.
* \note The default value is 0. This option applies to all importers.
* However, it is also possible to override the global setting
* for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME
* options (where XXX is a placeholder for the file format for which you
* want to override the global setting).
* Property type: integer.
*/
#define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "IMPORT_GLOBAL_KEYFRAME"
#define AI_CONFIG_IMPORT_MD3_KEYFRAME "IMPORT_MD3_KEYFRAME"
#define AI_CONFIG_IMPORT_MD2_KEYFRAME "IMPORT_MD2_KEYFRAME"
#define AI_CONFIG_IMPORT_MDL_KEYFRAME "IMPORT_MDL_KEYFRAME"
#define AI_CONFIG_IMPORT_MDC_KEYFRAME "IMPORT_MDC_KEYFRAME"
#define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME"
#define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME"
// ---------------------------------------------------------------------------
/** Smd load multiple animations
*
* Property type: bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_SMD_LOAD_ANIMATION_LIST "IMPORT_SMD_LOAD_ANIMATION_LIST"
// ---------------------------------------------------------------------------
/** @brief Configures the AC loader to collect all surfaces which have the
* "Backface cull" flag set in separate meshes.
*
* Property type: bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL \
"IMPORT_AC_SEPARATE_BFCULL"
// ---------------------------------------------------------------------------
/** @brief Configures whether the AC loader evaluates subdivision surfaces (
* indicated by the presence of the 'subdiv' attribute in the file). By
* default, Assimp performs the subdivision using the standard
* Catmull-Clark algorithm
*
* * Property type: bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION \
"IMPORT_AC_EVAL_SUBDIVISION"
// ---------------------------------------------------------------------------
/** @brief Configures the UNREAL 3D loader to separate faces with different
* surface flags (e.g. two-sided vs. single-sided).
*
* * Property type: bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS \
"UNREAL_HANDLE_FLAGS"
// ---------------------------------------------------------------------------
/** @brief Configures the terragen import plugin to compute uv's for
* terrains, if not given. Furthermore a default texture is assigned.
*
* UV coordinates for terrains are so simple to compute that you'll usually
* want to compute them on your own, if you need them. This option is intended
* for model viewers which want to offer an easy way to apply textures to
* terrains.
* * Property type: bool. Default value: false.
*/
#define AI_CONFIG_IMPORT_TER_MAKE_UVS \
"IMPORT_TER_MAKE_UVS"
// ---------------------------------------------------------------------------
/** @brief Configures the ASE loader to always reconstruct normal vectors
* basing on the smoothing groups loaded from the file.
*
* Some ASE files have carry invalid normals, other don't.
* * Property type: bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS \
"IMPORT_ASE_RECONSTRUCT_NORMALS"
// ---------------------------------------------------------------------------
/** @brief Configures the M3D loader to detect and process multi-part
* Quake player models.
*
* These models usually consist of 3 files, lower.md3, upper.md3 and
* head.md3. If this property is set to true, Assimp will try to load and
* combine all three files if one of them is loaded.
* Property type: bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART \
"IMPORT_MD3_HANDLE_MULTIPART"
// ---------------------------------------------------------------------------
/** @brief Tells the MD3 loader which skin files to load.
*
* When loading MD3 files, Assimp checks whether a file
* [md3_file_name]_[skin_name].skin is existing. These files are used by
* Quake III to be able to assign different skins (e.g. red and blue team)
* to models. 'default', 'red', 'blue' are typical skin names.
* Property type: String. Default value: "default".
*/
#define AI_CONFIG_IMPORT_MD3_SKIN_NAME \
"IMPORT_MD3_SKIN_NAME"
// ---------------------------------------------------------------------------
/** @brief Specify the Quake 3 shader file to be used for a particular
* MD3 file. This can also be a search path.
*
* By default Assimp's behaviour is as follows: If a MD3 file
* <tt>any_path/models/any_q3_subdir/model_name/file_name.md3</tt> is
* loaded, the library tries to locate the corresponding shader file in
* <tt>any_path/scripts/model_name.shader</tt>. This property overrides this
* behaviour. It can either specify a full path to the shader to be loaded
* or alternatively the path (relative or absolute) to the directory where
* the shaders for all MD3s to be loaded reside. Assimp attempts to open
* <tt>IMPORT_MD3_SHADER_SRC/model_name.shader</tt> first, <tt>IMPORT_MD3_SHADER_SRC/file_name.shader</tt>
* is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash.
* Property type: String. Default value: n/a.
*/
#define AI_CONFIG_IMPORT_MD3_SHADER_SRC \
"IMPORT_MD3_SHADER_SRC"
// ---------------------------------------------------------------------------
/** @brief Configures the LWO loader to load just one layer from the model.
*
* LWO files consist of layers and in some cases it could be useful to load
* only one of them. This property can be either a string - which specifies
* the name of the layer - or an integer - the index of the layer. If the
* property is not set the whole LWO model is loaded. Loading fails if the
* requested layer is not available. The layer index is zero-based and the
* layer name may not be empty.<br>
* Property type: Integer. Default value: all layers are loaded.
*/
#define AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY \
"IMPORT_LWO_ONE_LAYER_ONLY"
// ---------------------------------------------------------------------------
/** @brief Configures the MD5 loader to not load the MD5ANIM file for
* a MD5MESH file automatically.
*
* The default strategy is to look for a file with the same name but the
* MD5ANIM extension in the same directory. If it is found, it is loaded
* and combined with the MD5MESH file. This configuration option can be
* used to disable this behaviour.
*
* * Property type: bool. Default value: false.
*/
#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \
"IMPORT_MD5_NO_ANIM_AUTOLOAD"
// ---------------------------------------------------------------------------
/** @brief Defines the begin of the time range for which the LWS loader
* evaluates animations and computes aiNodeAnim's.
*
* Assimp provides full conversion of LightWave's envelope system, including
* pre and post conditions. The loader computes linearly subsampled animation
* chanels with the frame rate given in the LWS file. This property defines
* the start time. Note: animation channels are only generated if a node
* has at least one envelope with more tan one key assigned. This property.
* is given in frames, '0' is the first frame. By default, if this property
* is not set, the importer takes the animation start from the input LWS
* file ('FirstFrame' line)<br>
* Property type: Integer. Default value: taken from file.
*
* @see AI_CONFIG_IMPORT_LWS_ANIM_END - end of the imported time range
*/
#define AI_CONFIG_IMPORT_LWS_ANIM_START \
"IMPORT_LWS_ANIM_START"
#define AI_CONFIG_IMPORT_LWS_ANIM_END \
"IMPORT_LWS_ANIM_END"
// ---------------------------------------------------------------------------
/** @brief Defines the output frame rate of the IRR loader.
*
* IRR animations are difficult to convert for Assimp and there will
* always be a loss of quality. This setting defines how many keys per second
* are returned by the converter.<br>
* Property type: integer. Default value: 100
*/
#define AI_CONFIG_IMPORT_IRR_ANIM_FPS \
"IMPORT_IRR_ANIM_FPS"
// ---------------------------------------------------------------------------
/** @brief Ogre Importer will try to find referenced materials from this file.
*
* Ogre meshes reference with material names, this does not tell Assimp the file
* where it is located in. Assimp will try to find the source file in the following
* order: <material-name>.material, <mesh-filename-base>.material and
* lastly the material name defined by this config property.
* <br>
* Property type: String. Default value: Scene.material.
*/
#define AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE \
"IMPORT_OGRE_MATERIAL_FILE"
// ---------------------------------------------------------------------------
/** @brief Ogre Importer detect the texture usage from its filename.
*
* Ogre material texture units do not define texture type, the textures usage
* depends on the used shader or Ogre's fixed pipeline. If this config property
* is true Assimp will try to detect the type from the textures filename postfix:
* _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec,
* _specular and _specularmap for specular map, _l, _light, _lightmap, _occ
* and _occlusion for light map, _disp and _displacement for displacement map.
* The matching is case insensitive. Post fix is taken between the last
* underscore and the last period.
* Default behavior is to detect type from lower cased texture unit name by
* matching against: normalmap, specularmap, lightmap and displacementmap.
* For both cases if no match is found aiTextureType_DIFFUSE is used.
* <br>
* Property type: Bool. Default value: false.
*/
#define AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME \
"IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME"
/** @brief Specifies whether the Android JNI asset extraction is supported.
*
* Turn on this option if you want to manage assets in native
* Android application without having to keep the internal directory and asset
* manager pointer.
*/
#define AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT "AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT"
// ---------------------------------------------------------------------------
/** @brief Specifies whether the IFC loader skips over IfcSpace elements.
*
* IfcSpace elements (and their geometric representations) are used to
* represent, well, free space in a building storey.<br>
* Property type: Bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS"
// ---------------------------------------------------------------------------
/** @brief Specifies whether the IFC loader will use its own, custom triangulation
* algorithm to triangulate wall and floor meshes.
*
* If this property is set to false, walls will be either triangulated by
* #aiProcess_Triangulate or will be passed through as huge polygons with
* faked holes (i.e. holes that are connected with the outer boundary using
* a dummy edge). It is highly recommended to set this property to true
* if you want triangulated data because #aiProcess_Triangulate is known to
* have problems with the kind of polygons that the IFC loader spits out for
* complicated meshes.
* Property type: Bool. Default value: true.
*/
#define AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION "IMPORT_IFC_CUSTOM_TRIANGULATION"
// ---------------------------------------------------------------------------
/** @brief Set the tessellation conic angle for IFC smoothing curves.
*
* This is used by the IFC importer to determine the tessellation parameter
* for smoothing curves.
* @note The default value is AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE and the
* accepted values are in range [5.0, 120.0].
* Property type: Float.
*/
#define AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE "IMPORT_IFC_SMOOTHING_ANGLE"
// default value for AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE
#if (!defined AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE)
#define AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE 10.0f
#endif
// ---------------------------------------------------------------------------
/** @brief Set the tessellation for IFC cylindrical shapes.
*
* This is used by the IFC importer to determine the tessellation parameter
* for cylindrical shapes, i.e. the number of segments used to approximate a circle.
* @note The default value is AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION and the
* accepted values are in range [3, 180].
* Property type: Integer.
*/
#define AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION "IMPORT_IFC_CYLINDRICAL_TESSELLATION"
// default value for AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION
#if (!defined AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION)
#define AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION 32
#endif
// ---------------------------------------------------------------------------
/** @brief Specifies whether the Collada loader will ignore the provided up direction.
*
* If this property is set to true, the up direction provided in the file header will
* be ignored and the file will be loaded as is.
* Property type: Bool. Default value: false.
*/
#define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION"
// ---------------------------------------------------------------------------
/** @brief Specifies whether the Collada loader should use Collada names as node names.
*
* If this property is set to true, the Collada names will be used as the
* node name. The default is to use the id tag (resp. sid tag, if no id tag is present)
* instead.
* Property type: Bool. Default value: false.
*/
#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES"
// ---------- All the Export defines ------------
/** @brief Specifies the xfile use double for real values of float
*
* Property type: Bool. Default value: false.
*/
#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT"
/**
*
*/
#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"
/**
* @brief Specifies a gobal key factor for scale, float value
*/
#define AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY "GLOBAL_SCALE_FACTOR"
#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT)
#define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
#endif // !! AI_DEBONE_THRESHOLD
// ---------- All the Build/Compile-time defines ------------
/** @brief Specifies if double precision is supported inside assimp
*
* Property type: Bool. Default value: undefined.
*/
/* #cmakedefine ASSIMP_DOUBLE_PRECISION 1 */
#endif // !! AI_CONFIG_H_INC

616
thirdparty/assimp/code/BaseImporter.cpp vendored Normal file
View File

@ -0,0 +1,616 @@
/*
---------------------------------------------------------------------------
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() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
BaseImporter::~BaseImporter() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
aiScene* BaseImporter::ReadFile(const 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);
} 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;
}
#include "../contrib/utf8cpp/source/utf8.h"
// ------------------------------------------------------------------------------------------------
// 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 %%%");
}
}

107
thirdparty/assimp/code/BaseProcess.cpp vendored Normal file
View File

@ -0,0 +1,107 @@
/*
---------------------------------------------------------------------------
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 = NULL;
}
}
// ------------------------------------------------------------------------------------------------
void BaseProcess::SetupProperties(const Importer* /*pImp*/)
{
// the default implementation does nothing
}
// ------------------------------------------------------------------------------------------------
bool BaseProcess::RequireVerboseFormat() const
{
return true;
}

290
thirdparty/assimp/code/BaseProcess.h vendored Normal file
View File

@ -0,0 +1,290 @@
/*
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

155
thirdparty/assimp/code/Bitmap.cpp vendored Normal file
View File

@ -0,0 +1,155 @@
/*
---------------------------------------------------------------------------
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

@ -0,0 +1,136 @@
/*
---------------------------------------------------------------------------
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

@ -0,0 +1,99 @@
/*
---------------------------------------------------------------------------
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

@ -0,0 +1,319 @@
/*
---------------------------------------------------------------------------
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 post processing step to calculate
* tangents and bitangents for all imported meshes
*/
// internal headers
#include "CalcTangentsProcess.h"
#include "ProcessHelper.h"
#include <assimp/TinyFormatter.h>
#include <assimp/qnan.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
CalcTangentsProcess::CalcTangentsProcess()
: configMaxAngle( AI_DEG_TO_RAD(45.f) )
, configSourceUV( 0 ) {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
CalcTangentsProcess::~CalcTangentsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_CalcTangentSpace) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void CalcTangentsProcess::SetupProperties(const Importer* pImp)
{
ai_assert( NULL != pImp );
// get the current value of the property
configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f);
configMaxAngle = AI_DEG_TO_RAD(configMaxAngle);
configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void CalcTangentsProcess::Execute( aiScene* pScene)
{
ai_assert( NULL != pScene );
ASSIMP_LOG_DEBUG("CalcTangentsProcess begin");
bool bHas = false;
for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) {
if(ProcessMesh( pScene->mMeshes[a],a))bHas = true;
}
if ( bHas ) {
ASSIMP_LOG_INFO("CalcTangentsProcess finished. Tangents have been calculated");
} else {
ASSIMP_LOG_DEBUG("CalcTangentsProcess finished");
}
}
// ------------------------------------------------------------------------------------------------
// Calculates tangents and bi-tangents for the given mesh
bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
{
// we assume that the mesh is still in the verbose vertex format where each face has its own set
// of vertices and no vertices are shared between faces. Sadly I don't know any quick test to
// assert() it here.
// assert( must be verbose, dammit);
if (pMesh->mTangents) // this implies that mBitangents is also there
return false;
// If the mesh consists of lines and/or points but not of
// triangles or higher-order polygons the normal vectors
// are undefined.
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
{
ASSIMP_LOG_INFO("Tangents are undefined for line and point meshes");
return false;
}
// what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement
if( pMesh->mNormals == NULL)
{
ASSIMP_LOG_ERROR("Failed to compute tangents; need normals");
return false;
}
if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] )
{
ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV));
return false;
}
const float angleEpsilon = 0.9999f;
std::vector<bool> vertexDone( pMesh->mNumVertices, false);
const float qnan = get_qnan();
// create space for the tangents and bitangents
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
const aiVector3D* meshPos = pMesh->mVertices;
const aiVector3D* meshNorm = pMesh->mNormals;
const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV];
aiVector3D* meshTang = pMesh->mTangents;
aiVector3D* meshBitang = pMesh->mBitangents;
// calculate the tangent and bitangent for every face
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices < 3)
{
// There are less than three indices, thus the tangent vector
// is not defined. We are finished with these vertices now,
// their tangent vectors are set to qnan.
for (unsigned int i = 0; i < face.mNumIndices;++i)
{
unsigned int idx = face.mIndices[i];
vertexDone [idx] = true;
meshTang [idx] = aiVector3D(qnan);
meshBitang [idx] = aiVector3D(qnan);
}
continue;
}
// triangle or polygon... we always use only the first three indices. A polygon
// is supposed to be planar anyways....
// FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
// position differences p1->p2 and p1->p3
aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
// texture offset p1->p2 and p1->p3
float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
// when t1, t2, t3 in same position in UV space, just use default UV direction.
if ( sx * ty == sy * tx ) {
sx = 0.0; sy = 1.0;
tx = 1.0; ty = 0.0;
}
// tangent points in the direction where to positive X axis of the texture coord's would point in model space
// bitangent's points along the positive Y axis of the texture coord's, respectively
aiVector3D tangent, bitangent;
tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
// store for every vertex of that face
for( unsigned int b = 0; b < face.mNumIndices; ++b ) {
unsigned int p = face.mIndices[b];
// project tangent and bitangent into the plane formed by the vertex' normal
aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
localTangent.Normalize(); localBitangent.Normalize();
// reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z);
if (invalid_tangent != invalid_bitangent) {
if (invalid_tangent) {
localTangent = meshNorm[p] ^ localBitangent;
localTangent.Normalize();
} else {
localBitangent = localTangent ^ meshNorm[p];
localBitangent.Normalize();
}
}
// and write it into the mesh.
meshTang[ p ] = localTangent;
meshBitang[ p ] = localBitangent;
}
}
// create a helper to quickly find locally close vertices among the vertex array
// FIX: check whether we can reuse the SpatialSort of a previous step
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
float posEpsilon;
if (shared)
{
std::vector<std::pair<SpatialSort,float> >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf)
{
std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
vertexFinder = &blubb.first;
posEpsilon = blubb.second;;
}
}
if (!vertexFinder)
{
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
vertexFinder = &_vertexFinder;
posEpsilon = ComputePositionEpsilon(pMesh);
}
std::vector<unsigned int> verticesFound;
const float fLimit = std::cos(configMaxAngle);
std::vector<unsigned int> closeVertices;
// in the second pass we now smooth out all tangents and bitangents at the same local position
// if they are not too far off.
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
{
if( vertexDone[a])
continue;
const aiVector3D& origPos = pMesh->mVertices[a];
const aiVector3D& origNorm = pMesh->mNormals[a];
const aiVector3D& origTang = pMesh->mTangents[a];
const aiVector3D& origBitang = pMesh->mBitangents[a];
closeVertices.resize( 0 );
// find all vertices close to that position
vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
closeVertices.reserve (verticesFound.size()+5);
closeVertices.push_back( a);
// look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
for( unsigned int b = 0; b < verticesFound.size(); b++)
{
unsigned int idx = verticesFound[b];
if( vertexDone[idx])
continue;
if( meshNorm[idx] * origNorm < angleEpsilon)
continue;
if( meshTang[idx] * origTang < fLimit)
continue;
if( meshBitang[idx] * origBitang < fLimit)
continue;
// it's similar enough -> add it to the smoothing group
closeVertices.push_back( idx);
vertexDone[idx] = true;
}
// smooth the tangents and bitangents of all vertices that were found to be close enough
aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
for( unsigned int b = 0; b < closeVertices.size(); ++b)
{
smoothTangent += meshTang[ closeVertices[b] ];
smoothBitangent += meshBitang[ closeVertices[b] ];
}
smoothTangent.Normalize();
smoothBitangent.Normalize();
// and write it back into all affected tangents
for( unsigned int b = 0; b < closeVertices.size(); ++b)
{
meshTang[ closeVertices[b] ] = smoothTangent;
meshBitang[ closeVertices[b] ] = smoothBitangent;
}
}
return true;
}

View File

@ -0,0 +1,117 @@
/*
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 calculate tangents and
bitangents on all imported meshes.*/
#ifndef AI_CALCTANGENTSPROCESS_H_INC
#define AI_CALCTANGENTSPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex
* of all meshes. It is expected to be run before the JoinVerticesProcess runs
* because the joining of vertices also considers tangents and bitangents for
* uniqueness.
*/
class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess
{
public:
CalcTangentsProcess();
~CalcTangentsProcess();
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.
*/
void SetupProperties(const Importer* pImp);
// setter for configMaxAngle
inline void SetMaxSmoothAngle(float f)
{
configMaxAngle =f;
}
protected:
// -------------------------------------------------------------------
/** Calculates tangents and bitangents for a specific mesh.
* @param pMesh The mesh to process.
* @param meshIndex Index of the mesh
*/
bool ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
private:
/** Configuration option: maximum smoothing angle, in radians*/
float configMaxAngle;
unsigned int configSourceUV;
};
} // end of namespace Assimp
#endif // AI_CALCTANGENTSPROCESS_H_INC

View File

@ -0,0 +1,506 @@
/*
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 GenUVCoords step */
#include "ComputeUVMappingProcess.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h>
using namespace Assimp;
namespace {
const static aiVector3D base_axis_y(0.0,1.0,0.0);
const static aiVector3D base_axis_x(1.0,0.0,0.0);
const static aiVector3D base_axis_z(0.0,0.0,1.0);
const static ai_real angle_epsilon = ai_real( 0.95 );
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ComputeUVMappingProcess::ComputeUVMappingProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
ComputeUVMappingProcess::~ComputeUVMappingProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_GenUVCoords) != 0;
}
// ------------------------------------------------------------------------------------------------
// Check whether a ray intersects a plane and find the intersection point
inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
const aiVector3D& planeNormal, aiVector3D& pos)
{
const ai_real b = planeNormal * (planePos - ray.pos);
ai_real h = ray.dir * planeNormal;
if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
return false;
pos = ray.pos + (ray.dir * h);
return true;
}
// ------------------------------------------------------------------------------------------------
// Find the first empty UV channel in a mesh
inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
{
for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
if (!mesh->mTextureCoords[m])return m;
ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found");
return UINT_MAX;
}
// ------------------------------------------------------------------------------------------------
// Try to remove UV seams
void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
{
// TODO: just a very rough algorithm. I think it could be done
// much easier, but I don't know how and am currently too tired to
// to think about a better solution.
const static ai_real LOWER_LIMIT = ai_real( 0.1 );
const static ai_real UPPER_LIMIT = ai_real( 0.9 );
const static ai_real LOWER_EPSILON = ai_real( 10e-3 );
const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 );
for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
{
const aiFace& face = mesh->mFaces[fidx];
if (face.mNumIndices < 3) continue; // triangles and polygons only, please
unsigned int small = face.mNumIndices, large = small;
bool zero = false, one = false, round_to_zero = false;
// Check whether this face lies on a UV seam. We can just guess,
// but the assumption that a face with at least one very small
// on the one side and one very large U coord on the other side
// lies on a UV seam should work for most cases.
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
if (out[face.mIndices[n]].x < LOWER_LIMIT)
{
small = n;
// If we have a U value very close to 0 we can't
// round the others to 0, too.
if (out[face.mIndices[n]].x <= LOWER_EPSILON)
zero = true;
else round_to_zero = true;
}
if (out[face.mIndices[n]].x > UPPER_LIMIT)
{
large = n;
// If we have a U value very close to 1 we can't
// round the others to 1, too.
if (out[face.mIndices[n]].x >= UPPER_EPSILON)
one = true;
}
}
if (small != face.mNumIndices && large != face.mNumIndices)
{
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
// If the u value is over the upper limit and no other u
// value of that face is 0, round it to 0
if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
out[face.mIndices[n]].x = 0.0;
// If the u value is below the lower limit and no other u
// value of that face is 1, round it to 1
else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one)
out[face.mIndices[n]].x = 1.0;
// The face contains both 0 and 1 as UV coords. This can occur
// for faces which have an edge that lies directly on the seam.
// Due to numerical inaccuracies one U coord becomes 0, the
// other 1. But we do still have a third UV coord to determine
// to which side we must round to.
else if (one && zero)
{
if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
out[face.mIndices[n]].x = 0.0;
else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
out[face.mIndices[n]].x = 1.0;
}
}
}
}
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
{
aiVector3D center, min, max;
FindMeshCenter(mesh, center, min, max);
// If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
// currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) {
// For each point get a normalized projection vector in the sphere,
// get its longitude and latitude and map them to their respective
// UV axes. Problems occur around the poles ... unsolvable.
//
// The spherical coordinate system looks like this:
// x = cos(lon)*cos(lat)
// y = sin(lon)*cos(lat)
// z = sin(lat)
//
// Thus we can derive:
// lat = arcsin (z)
// lon = arctan (y/x)
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
else if (axis * base_axis_y >= angle_epsilon) {
// ... just the same again
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
else if (axis * base_axis_z >= angle_epsilon) {
// ... just the same again
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
// slower code path in case the mapping axis is not one of the coordinate system axes
else {
aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
// again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
(std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
}
}
// Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the
// other side.
RemoveUVSeams(mesh,out);
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
{
aiVector3D center, min, max;
// If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
// currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.x - min.x;
// If the main axis is 'z', the z coordinate of a point 'p' is mapped
// directly to the texture V axis. The other axis is derived from
// the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
// 'c' is the center point of the mesh.
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
uv.y = (pos.x - min.x) / diff;
uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
}
}
else if (axis * base_axis_y >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.y - min.y;
// just the same ...
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
uv.y = (pos.y - min.y) / diff;
uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
}
}
else if (axis * base_axis_z >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.z - min.z;
// just the same ...
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
uv.y = (pos.z - min.z) / diff;
uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
}
}
// slower code path in case the mapping axis is not one of the coordinate system axes
else {
aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
const ai_real diff = max.y - min.y;
// again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){
const aiVector3D pos = mTrafo* mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
uv.y = (pos.y - min.y) / diff;
uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
}
}
// Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the
// other side.
RemoveUVSeams(mesh,out);
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
{
ai_real diffu,diffv;
aiVector3D center, min, max;
// If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
// currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
diffu = max.z - min.z;
diffv = max.y - min.y;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0);
}
}
else if (axis * base_axis_y >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
diffu = max.x - min.x;
diffv = max.z - min.z;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
}
}
else if (axis * base_axis_z >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max);
diffu = max.y - min.y;
diffv = max.z - min.z;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt];
out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0);
}
}
// slower code path in case the mapping axis is not one of the coordinate system axes
else
{
aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
diffu = max.x - min.x;
diffv = max.z - min.z;
// again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
}
}
// shouldn't be necessary to remove UV seams ...
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* )
{
ASSIMP_LOG_ERROR("Mapping type currently not implemented");
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin");
char buffer[1024];
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
std::list<MappingInfo> mappingStack;
/* Iterate through all materials and search for non-UV mapped textures
*/
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
{
mappingStack.clear();
aiMaterial* mat = pScene->mMaterials[i];
for (unsigned int a = 0; a < mat->mNumProperties;++a)
{
aiMaterialProperty* prop = mat->mProperties[a];
if (!::strcmp( prop->mKey.data, "$tex.mapping"))
{
aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
if (aiTextureMapping_UV != mapping)
{
if (!DefaultLogger::isNullLogger())
{
ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s",
TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
MappingTypeToString(mapping));
ASSIMP_LOG_INFO(buffer);
}
if (aiTextureMapping_OTHER == mapping)
continue;
MappingInfo info (mapping);
// Get further properties - currently only the major axis
for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
{
aiMaterialProperty* prop2 = mat->mProperties[a2];
if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
continue;
if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) {
info.axis = *((aiVector3D*)prop2->mData);
break;
}
}
unsigned int idx( 99999999 );
// Check whether we have this mapping mode already
std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
if (mappingStack.end() != it)
{
idx = (*it).uv;
}
else
{
/* We have found a non-UV mapped texture. Now
* we need to find all meshes using this material
* that we can compute UV channels for them.
*/
for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
{
aiMesh* mesh = pScene->mMeshes[m];
unsigned int outIdx = 0;
if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX ||
!mesh->mNumVertices)
{
continue;
}
// Allocate output storage
aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
switch (mapping)
{
case aiTextureMapping_SPHERE:
ComputeSphereMapping(mesh,info.axis,p);
break;
case aiTextureMapping_CYLINDER:
ComputeCylinderMapping(mesh,info.axis,p);
break;
case aiTextureMapping_PLANE:
ComputePlaneMapping(mesh,info.axis,p);
break;
case aiTextureMapping_BOX:
ComputeBoxMapping(mesh,p);
break;
default:
ai_assert(false);
}
if (m && idx != outIdx)
{
ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to "
"this material have equal numbers of UV channels. The UV index stored in "
"the material structure does therefore not apply for all meshes. ");
}
idx = outIdx;
}
info.uv = idx;
mappingStack.push_back(info);
}
// Update the material property list
mapping = aiTextureMapping_UV;
((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
}
}
}
}
ASSIMP_LOG_DEBUG("GenUVCoordsProcess finished");
}

View File

@ -0,0 +1,148 @@
/*
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 compute UV coordinates
from abstract mappings, such as box or spherical*/
#ifndef AI_COMPUTEUVMAPPING_H_INC
#define AI_COMPUTEUVMAPPING_H_INC
#include "BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/material.h>
#include <assimp/types.h>
class ComputeUVMappingTest;
namespace Assimp {
// ---------------------------------------------------------------------------
/** ComputeUVMappingProcess - converts special mappings, such as spherical,
* cylindrical or boxed to proper UV coordinates for rendering.
*/
class ComputeUVMappingProcess : public BaseProcess
{
public:
ComputeUVMappingProcess();
~ComputeUVMappingProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @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;
// -------------------------------------------------------------------
/** 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);
protected:
// -------------------------------------------------------------------
/** Computes spherical UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @param axis Main axis
* @param out Receives output UV coordinates
*/
void ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis,
aiVector3D* out);
// -------------------------------------------------------------------
/** Computes cylindrical UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @param axis Main axis
* @param out Receives output UV coordinates
*/
void ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis,
aiVector3D* out);
// -------------------------------------------------------------------
/** Computes planar UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @param axis Main axis
* @param out Receives output UV coordinates
*/
void ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis,
aiVector3D* out);
// -------------------------------------------------------------------
/** Computes cubic UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @param out Receives output UV coordinates
*/
void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out);
private:
// temporary structure to describe a mapping
struct MappingInfo
{
explicit MappingInfo(aiTextureMapping _type)
: type (_type)
, axis (0.f,1.f,0.f)
, uv (0u)
{}
aiTextureMapping type;
aiVector3D axis;
unsigned int uv;
bool operator== (const MappingInfo& other)
{
return type == other.type && axis == other.axis;
}
};
};
} // end of namespace Assimp
#endif // AI_COMPUTEUVMAPPING_H_INC

View File

@ -0,0 +1,414 @@
/*
---------------------------------------------------------------------------
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 MakeLeftHandedProcess.cpp
* @brief Implementation of the post processing step to convert all
* imported data to a left-handed coordinate system.
*
* Face order & UV flip are also implemented here, for the sake of a
* better location.
*/
#include "ConvertToLHProcess.h"
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
namespace {
template <typename aiMeshType>
void flipUVs(aiMeshType* pMesh) {
if (pMesh == nullptr) { return; }
// mirror texture y coordinate
for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) {
if (!pMesh->HasTextureCoords(tcIdx)) {
break;
}
for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) {
pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y;
}
}
}
} // namespace
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MakeLeftHandedProcess::MakeLeftHandedProcess()
: BaseProcess() {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
MakeLeftHandedProcess::~MakeLeftHandedProcess() {
// empty
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const
{
return 0 != (pFlags & aiProcess_MakeLeftHanded);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void MakeLeftHandedProcess::Execute( aiScene* pScene)
{
// Check for an existent root node to proceed
ai_assert(pScene->mRootNode != NULL);
ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin");
// recursively convert all the nodes
ProcessNode( pScene->mRootNode, aiMatrix4x4());
// process the meshes accordingly
for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
ProcessMesh( pScene->mMeshes[ a ] );
}
// process the materials accordingly
for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) {
ProcessMaterial( pScene->mMaterials[ a ] );
}
// transform all animation channels as well
for( unsigned int a = 0; a < pScene->mNumAnimations; a++)
{
aiAnimation* anim = pScene->mAnimations[a];
for( unsigned int b = 0; b < anim->mNumChannels; b++)
{
aiNodeAnim* nodeAnim = anim->mChannels[b];
ProcessAnimation( nodeAnim);
}
}
ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished");
}
// ------------------------------------------------------------------------------------------------
// Recursively converts a node, all of its children and all of its meshes
void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation)
{
// mirror all base vectors at the local Z axis
pNode->mTransformation.c1 = -pNode->mTransformation.c1;
pNode->mTransformation.c2 = -pNode->mTransformation.c2;
pNode->mTransformation.c3 = -pNode->mTransformation.c3;
pNode->mTransformation.c4 = -pNode->mTransformation.c4;
// now invert the Z axis again to keep the matrix determinant positive.
// The local meshes will be inverted accordingly so that the result should look just fine again.
pNode->mTransformation.a3 = -pNode->mTransformation.a3;
pNode->mTransformation.b3 = -pNode->mTransformation.b3;
pNode->mTransformation.c3 = -pNode->mTransformation.c3;
pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways...
// continue for all children
for( size_t a = 0; a < pNode->mNumChildren; ++a ) {
ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation );
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single mesh to left handed coordinates.
void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) {
if ( nullptr == pMesh ) {
ASSIMP_LOG_ERROR( "Nullptr to mesh found." );
return;
}
// mirror positions, normals and stuff along the Z axis
for( size_t a = 0; a < pMesh->mNumVertices; ++a)
{
pMesh->mVertices[a].z *= -1.0f;
if (pMesh->HasNormals()) {
pMesh->mNormals[a].z *= -1.0f;
}
if( pMesh->HasTangentsAndBitangents())
{
pMesh->mTangents[a].z *= -1.0f;
pMesh->mBitangents[a].z *= -1.0f;
}
}
// mirror anim meshes positions, normals and stuff along the Z axis
for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m)
{
for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a)
{
pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f;
if (pMesh->mAnimMeshes[m]->HasNormals()) {
pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f;
}
if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents())
{
pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f;
pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f;
}
}
}
// mirror offset matrices of all bones
for( size_t a = 0; a < pMesh->mNumBones; ++a)
{
aiBone* bone = pMesh->mBones[a];
bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3;
bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3;
bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3;
bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1;
bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2;
bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4;
}
// mirror bitangents as well as they're derived from the texture coords
if( pMesh->HasTangentsAndBitangents())
{
for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
pMesh->mBitangents[a] *= -1.0f;
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single material to left handed coordinates.
void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) {
if ( nullptr == _mat ) {
ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." );
return;
}
aiMaterial* mat = (aiMaterial*)_mat;
for (unsigned int a = 0; a < mat->mNumProperties;++a) {
aiMaterialProperty* prop = mat->mProperties[a];
// Mapping axis for UV mappings?
if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) {
ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */
aiVector3D* pff = (aiVector3D*)prop->mData;
pff->z *= -1.f;
}
}
}
// ------------------------------------------------------------------------------------------------
// Converts the given animation to LH coordinates.
void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim)
{
// position keys
for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
pAnim->mPositionKeys[a].mValue.z *= -1.0f;
// rotation keys
for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
{
/* That's the safe version, but the float errors add up. So we try the short version instead
aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
aiQuaternion rotquat( rotmat);
pAnim->mRotationKeys[a].mValue = rotquat;
*/
pAnim->mRotationKeys[a].mValue.x *= -1.0f;
pAnim->mRotationKeys[a].mValue.y *= -1.0f;
}
}
#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS
// # FlipUVsProcess
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FlipUVsProcess::FlipUVsProcess()
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FlipUVsProcess::~FlipUVsProcess()
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FlipUVsProcess::IsActive( unsigned int pFlags) const
{
return 0 != (pFlags & aiProcess_FlipUVs);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FlipUVsProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("FlipUVsProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
ProcessMesh(pScene->mMeshes[i]);
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
ProcessMaterial(pScene->mMaterials[i]);
ASSIMP_LOG_DEBUG("FlipUVsProcess finished");
}
// ------------------------------------------------------------------------------------------------
// Converts a single material
void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
{
aiMaterial* mat = (aiMaterial*)_mat;
for (unsigned int a = 0; a < mat->mNumProperties;++a) {
aiMaterialProperty* prop = mat->mProperties[a];
if( !prop ) {
ASSIMP_LOG_DEBUG( "Property is null" );
continue;
}
// UV transformation key?
if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) {
ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */
aiUVTransform* uv = (aiUVTransform*)prop->mData;
// just flip it, that's everything
uv->mTranslation.y *= -1.f;
uv->mRotation *= -1.f;
}
}
}
// ------------------------------------------------------------------------------------------------
// Converts a single mesh
void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
{
flipUVs(pMesh);
for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) {
flipUVs(pMesh->mAnimMeshes[idx]);
}
}
#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS
#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
// # FlipWindingOrderProcess
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FlipWindingOrderProcess::FlipWindingOrderProcess()
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FlipWindingOrderProcess::~FlipWindingOrderProcess()
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const
{
return 0 != (pFlags & aiProcess_FlipWindingOrder);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FlipWindingOrderProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
ProcessMesh(pScene->mMeshes[i]);
ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished");
}
// ------------------------------------------------------------------------------------------------
// Converts a single mesh
void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh)
{
// invert the order of all faces in this mesh
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for (unsigned int b = 0; b < face.mNumIndices / 2; b++) {
std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]);
}
}
// invert the order of all components in this mesh anim meshes
for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) {
aiAnimMesh* animMesh = pMesh->mAnimMeshes[m];
unsigned int numVertices = animMesh->mNumVertices;
if (animMesh->HasPositions()) {
for (unsigned int a = 0; a < numVertices; a++)
{
std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]);
}
}
if (animMesh->HasNormals()) {
for (unsigned int a = 0; a < numVertices; a++)
{
std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]);
}
}
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) {
if (animMesh->HasTextureCoords(i)) {
for (unsigned int a = 0; a < numVertices; a++)
{
std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]);
}
}
}
if (animMesh->HasTangentsAndBitangents()) {
for (unsigned int a = 0; a < numVertices; a++)
{
std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]);
std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]);
}
}
for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) {
if (animMesh->HasVertexColors(v)) {
for (unsigned int a = 0; a < numVertices; a++)
{
std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]);
}
}
}
}
}
#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS

View File

@ -0,0 +1,170 @@
/*
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 MakeLeftHandedProcess.h
* @brief Defines a bunch of post-processing steps to handle
* coordinate system conversions.
*
* - LH to RH
* - UV origin upper-left to lower-left
* - face order cw to ccw
*/
#ifndef AI_CONVERTTOLHPROCESS_H_INC
#define AI_CONVERTTOLHPROCESS_H_INC
#include <assimp/types.h>
#include "BaseProcess.h"
struct aiMesh;
struct aiNodeAnim;
struct aiNode;
struct aiMaterial;
namespace Assimp {
// -----------------------------------------------------------------------------------
/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed
* coordinate system.
*
* This implies a mirroring of the Z axis of the coordinate system. But to keep
* transformation matrices free from reflections we shift the reflection to other
* places. We mirror the meshes and adapt the rotations.
*
* @note RH-LH and LH-RH is the same, so this class can be used for both
*/
class MakeLeftHandedProcess : public BaseProcess
{
public:
MakeLeftHandedProcess();
~MakeLeftHandedProcess();
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
protected:
// -------------------------------------------------------------------
/** Recursively converts a node and all of its children
*/
void ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation);
// -------------------------------------------------------------------
/** Converts a single mesh to left handed coordinates.
* This means that positions, normals and tangents are mirrored at
* the local Z axis and the order of all faces are inverted.
* @param pMesh The mesh to convert.
*/
void ProcessMesh( aiMesh* pMesh);
// -------------------------------------------------------------------
/** Converts a single material to left-handed coordinates
* @param pMat Material to convert
*/
void ProcessMaterial( aiMaterial* pMat);
// -------------------------------------------------------------------
/** Converts the given animation to LH coordinates.
* The rotation and translation keys are transformed, the scale keys
* work in local space and can therefore be left untouched.
* @param pAnim The bone animation to transform
*/
void ProcessAnimation( aiNodeAnim* pAnim);
};
// ---------------------------------------------------------------------------
/** Postprocessing step to flip the face order of the imported data
*/
class FlipWindingOrderProcess : public BaseProcess
{
friend class Importer;
public:
/** Constructor to be privately used by Importer */
FlipWindingOrderProcess();
/** Destructor, private as well */
~FlipWindingOrderProcess();
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
protected:
void ProcessMesh( aiMesh* pMesh);
};
// ---------------------------------------------------------------------------
/** Postprocessing step to flip the UV coordinate system of the import data
*/
class FlipUVsProcess : public BaseProcess
{
friend class Importer;
public:
/** Constructor to be privately used by Importer */
FlipUVsProcess();
/** Destructor, private as well */
~FlipUVsProcess();
// -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
void Execute( aiScene* pScene);
protected:
void ProcessMesh( aiMesh* pMesh);
void ProcessMaterial( aiMaterial* mat);
};
} // end of namespace Assimp
#endif // AI_CONVERTTOLHPROCESS_H_INC

View File

@ -0,0 +1,92 @@
/*
---------------------------------------------------------------------------
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->mVertices = NULL;
animesh->mNormals = NULL;
animesh->mTangents = NULL;
animesh->mBitangents = NULL;
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

465
thirdparty/assimp/code/DeboneProcess.cpp vendored Normal file
View File

@ -0,0 +1,465 @@
/*
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 DeboneProcess.cpp
/** Implementation of the DeboneProcess post processing step */
// internal headers of the post-processing framework
#include "ProcessHelper.h"
#include "DeboneProcess.h"
#include <stdio.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
DeboneProcess::DeboneProcess()
{
mNumBones = 0;
mNumBonesCanDoWithout = 0;
mThreshold = AI_DEBONE_THRESHOLD;
mAllOrNone = false;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
DeboneProcess::~DeboneProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool DeboneProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_Debone) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void DeboneProcess::SetupProperties(const Importer* pImp)
{
// get the current value of the property
mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false;
mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void DeboneProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("DeboneProcess begin");
if(!pScene->mNumMeshes) {
return;
}
std::vector<bool> splitList(pScene->mNumMeshes);
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
splitList[a] = ConsiderMesh( pScene->mMeshes[a] );
}
int numSplits = 0;
if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) {
for(unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if(splitList[a]) {
numSplits++;
}
}
}
if(numSplits) {
// we need to do something. Let's go.
//mSubMeshIndices.clear(); // really needed?
mSubMeshIndices.resize(pScene->mNumMeshes); // because we're doing it here anyway
// 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<std::pair<aiMesh*,const aiBone*> > newMeshes;
if(splitList[a]) {
SplitMesh(srcMesh,newMeshes);
}
// mesh was split
if(!newMeshes.empty()) {
unsigned int out = 0, in = srcMesh->mNumBones;
// store new meshes and indices of the new meshes
for(unsigned int b=0;b<newMeshes.size();b++) {
const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0;
aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0;
std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode);
mSubMeshIndices[a].push_back(push_pair);
meshes.push_back(newMeshes[b].first);
out+=newMeshes[b].first->mNumBones;
}
if(!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out);
}
// 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(std::pair<unsigned int,aiNode*>(static_cast<unsigned int>(meshes.size()),(aiNode*)0));
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("DeboneProcess end");
}
// ------------------------------------------------------------------------------------------------
// Counts bones total/removable in a given mesh.
bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
{
if(!pMesh->HasBones()) {
return false;
}
bool split = false;
//interstitial faces not permitted
bool isInterstitialRequired = false;
std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
const unsigned int cUnowned = UINT_MAX;
const unsigned int cCoowned = UINT_MAX-1;
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
float w = pMesh->mBones[i]->mWeights[j].mWeight;
if(w==0.0f) {
continue;
}
unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
if(w>=mThreshold) {
if(vertexBones[vid]!=cUnowned) {
if(vertexBones[vid]==i) //double entry
{
ASSIMP_LOG_WARN("Encountered double entry in bone weights");
}
else //TODO: track attraction in order to break tie
{
vertexBones[vid] = cCoowned;
}
}
else vertexBones[vid] = i;
}
if(!isBoneNecessary[i]) {
isBoneNecessary[i] = w<mThreshold;
}
}
if(!isBoneNecessary[i]) {
isInterstitialRequired = true;
}
}
if(isInterstitialRequired) {
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
if(v!=w) {
if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
}
}
}
}
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
if(!isBoneNecessary[i]) {
mNumBonesCanDoWithout++;
split = true;
}
mNumBones++;
}
return split;
}
// ------------------------------------------------------------------------------------------------
// Splits the given mesh by bone count.
void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const
{
// same deal here as ConsiderMesh basically
std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
const unsigned int cUnowned = UINT_MAX;
const unsigned int cCoowned = UINT_MAX-1;
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
float w = pMesh->mBones[i]->mWeights[j].mWeight;
if(w==0.0f) {
continue;
}
unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
if(w>=mThreshold) {
if(vertexBones[vid]!=cUnowned) {
if(vertexBones[vid]==i) //double entry
{
ASSIMP_LOG_WARN("Encountered double entry in bone weights");
}
else //TODO: track attraction in order to break tie
{
vertexBones[vid] = cCoowned;
}
}
else vertexBones[vid] = i;
}
if(!isBoneNecessary[i]) {
isBoneNecessary[i] = w<mThreshold;
}
}
}
unsigned int nFacesUnowned = 0;
std::vector<unsigned int> faceBones(pMesh->mNumFaces,UINT_MAX);
std::vector<unsigned int> facesPerBone(pMesh->mNumBones,0);
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
unsigned int nInterstitial = 1;
unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
if(v!=w) {
if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
}
else nInterstitial++;
}
if(v<pMesh->mNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices) {
faceBones[i] = v; //primitive belongs to bone #v
facesPerBone[v]++;
}
else nFacesUnowned++;
}
// invalidate any "cojoined" faces
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
if(faceBones[i]<pMesh->mNumBones&&isBoneNecessary[faceBones[i]])
{
ai_assert(facesPerBone[faceBones[i]]>0);
facesPerBone[faceBones[i]]--;
nFacesUnowned++;
faceBones[i] = cUnowned;
}
}
if(nFacesUnowned) {
std::vector<unsigned int> subFaces;
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
if(faceBones[i]==cUnowned) {
subFaces.push_back(i);
}
}
aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0);
std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,(const aiBone*)0);
poNewMeshes.push_back(push_pair);
}
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
if(!isBoneNecessary[i]&&facesPerBone[i]>0) {
std::vector<unsigned int> subFaces;
for(unsigned int j=0;j<pMesh->mNumFaces;j++) {
if(faceBones[j]==i) {
subFaces.push_back(j);
}
}
unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES;
aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f);
//Lifted from PretransformVertices.cpp
ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix);
std::pair<aiMesh*,const aiBone*> push_pair(subMesh,pMesh->mBones[i]);
poNewMeshes.push_back(push_pair);
}
}
}
// ------------------------------------------------------------------------------------------------
// Recursively updates the node's mesh list to account for the changed mesh list
void DeboneProcess::UpdateNode(aiNode* pNode) const
{
// rebuild the node's mesh index list
std::vector<unsigned int> newMeshList;
// this will require two passes
unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size());
// first pass, look for meshes which have not moved
for(unsigned int a=0;a<m;a++) {
unsigned int srcIndex = pNode->mMeshes[a];
const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex];
unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
for(unsigned int b=0;b<nSubmeshes;b++) {
if(!subMeshes[b].second) {
newMeshList.push_back(subMeshes[b].first);
}
}
}
// second pass, collect deboned meshes
for(unsigned int a=0;a<n;a++)
{
const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a];
unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
for(unsigned int b=0;b<nSubmeshes;b++) {
if(subMeshes[b].second == pNode) {
newMeshList.push_back(subMeshes[b].first);
}
}
}
if( pNode->mNumMeshes > 0 ) {
delete [] pNode->mMeshes; pNode->mMeshes = NULL;
}
pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());
if(pNode->mNumMeshes) {
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]);
}
}
// ------------------------------------------------------------------------------------------------
// Apply the node transformation to a mesh
void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const
{
// Check whether we need to transform the coordinates at all
if (!mat.IsIdentity()) {
if (mesh->HasPositions()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i];
}
}
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
aiMatrix4x4 mWorldIT = mat;
mWorldIT.Inverse().Transpose();
// TODO: implement Inverse() for aiMatrix3x3
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
}
}
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
}
}
}
}
}

134
thirdparty/assimp/code/DeboneProcess.h vendored Normal file
View File

@ -0,0 +1,134 @@
/*
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.
----------------------------------------------------------------------
*/
/** Defines a post processing step to limit the number of bones affecting a single vertex. */
#ifndef AI_DEBONEPROCESS_H_INC
#define AI_DEBONEPROCESS_H_INC
#include <vector>
#include <utility>
#include "BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
class DeboneTest;
namespace Assimp
{
#if (!defined AI_DEBONE_THRESHOLD)
# define AI_DEBONE_THRESHOLD 1.0f
#endif // !! AI_DEBONE_THRESHOLD
// ---------------------------------------------------------------------------
/** This post processing step removes bones nearly losslessly or according to
* a configured threshold. In order to remove the bone, the primitives affected by
* the bone are split from the mesh. The split off (new) mesh is boneless. At any
* point in time, bones without affect upon a given mesh are to be removed.
*/
class DeboneProcess : public BaseProcess
{
public:
DeboneProcess();
~DeboneProcess();
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.
*/
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);
// -------------------------------------------------------------------
/** Counts bones total/removable in a given mesh.
* @param pMesh The mesh to process.
*/
bool ConsiderMesh( const aiMesh* pMesh);
/// 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< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const;
/// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode(aiNode* pNode) const;
// -------------------------------------------------------------------
// Apply transformation to a mesh
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const;
public:
/** Number of bones present in the scene. */
unsigned int mNumBones;
unsigned int mNumBonesCanDoWithout;
float mThreshold;
bool mAllOrNone;
/// Per mesh index: Array of indices of the new submeshes.
std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices;
};
} // end of namespace Assimp
#endif // AI_DEBONEPROCESS_H_INC

View File

@ -0,0 +1,154 @@
/*
---------------------------------------------------------------------------
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

@ -0,0 +1,257 @@
/*
---------------------------------------------------------------------------
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;
// maximum path length
// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
#ifdef PATH_MAX
# define PATHLIMIT PATH_MAX
#else
# define PATHLIMIT 4096
#endif
// ------------------------------------------------------------------------------------------------
// Tests for the existence of a file at the given path.
bool DefaultIOSystem::Exists( const char* pFile) const
{
#ifdef _WIN32
wchar_t fileName16[PATHLIMIT];
#ifndef WindowsStore
bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0;
if (isUnicode) {
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
struct __stat64 filestat;
if (0 != _wstat64(fileName16, &filestat)) {
return false;
}
} else {
#endif
FILE* file = ::fopen(pFile, "rb");
if (!file)
return false;
::fclose(file);
#ifndef WindowsStore
}
#endif
#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(NULL != strFile);
ai_assert(NULL != strMode);
FILE* file;
#ifdef _WIN32
wchar_t fileName16[PATHLIMIT];
#ifndef WindowsStore
bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0;
if (isUnicode) {
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
std::string mode8(strMode);
file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
} else {
#endif
file = ::fopen(strFile, strMode);
#ifndef WindowsStore
}
#endif
#else
file = ::fopen(strFile, strMode);
#endif
if (nullptr == file)
return nullptr;
return new DefaultIOStream(file, (std::string) 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 void MakeAbsolutePath (const char* in, char* _out)
{
ai_assert(in && _out);
#if defined( _MSC_VER ) || defined( __MINGW32__ )
#ifndef WindowsStore
bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0;
if (isUnicode) {
wchar_t out16[PATHLIMIT];
wchar_t in16[PATHLIMIT];
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT);
wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT);
if (ret) {
WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr);
}
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));
strcpy(_out, in);
}
} else {
#endif
char* ret = :: _fullpath(_out, in, PATHLIMIT);
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));
strcpy(_out, in);
}
#ifndef WindowsStore
}
#endif
#else
// use realpath
char* ret = realpath(in, _out);
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));
strcpy(_out,in);
}
#endif
}
// ------------------------------------------------------------------------------------------------
// 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;
char temp1[PATHLIMIT];
char temp2[PATHLIMIT];
MakeAbsolutePath (one, temp1);
MakeAbsolutePath (second, temp2);
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 != ret.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;
}
// ------------------------------------------------------------------------------------------------
#undef PATHLIMIT

418
thirdparty/assimp/code/DefaultLogger.cpp vendored Normal file
View File

@ -0,0 +1,418 @@
/*
---------------------------------------------------------------------------
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

@ -0,0 +1,65 @@
/*
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

@ -0,0 +1,109 @@
/*
---------------------------------------------------------------------------
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 post processing step to drop face
* normals for all imported faces.
*/
#include "DropFaceNormalsProcess.h"
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exceptional.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
DropFaceNormalsProcess::DropFaceNormalsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
DropFaceNormalsProcess::~DropFaceNormalsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const {
return (pFlags & aiProcess_DropNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void DropFaceNormalsProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
}
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]);
}
if (bHas) {
ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. "
"Face normals have been removed");
} else {
ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. "
"No normals were present");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* pMesh) {
if (NULL == pMesh->mNormals) {
return false;
}
delete[] pMesh->mNormals;
pMesh->mNormals = nullptr;
return true;
}

View File

@ -0,0 +1,86 @@
/*
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 compute face normals for all loaded faces*/
#ifndef AI_DROPFACENORMALPROCESS_H_INC
#define AI_DROPFACENORMALPROCESS_H_INC
#include "BaseProcess.h"
#include <assimp/mesh.h>
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The DropFaceNormalsProcess computes face normals for all faces of all meshes
*/
class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess
{
public:
DropFaceNormalsProcess();
~DropFaceNormalsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @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;
// -------------------------------------------------------------------
/** 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);
private:
bool DropMeshFaceNormals(aiMesh* pcMesh);
};
} // end of namespace Assimp
#endif // !!AI_DROPFACENORMALPROCESS_H_INC

View File

@ -0,0 +1,152 @@
/*
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 "EmbedTexturesProcess.h"
#include <assimp/ParsingUtils.h>
#include "ProcessHelper.h"
#include <fstream>
using namespace Assimp;
EmbedTexturesProcess::EmbedTexturesProcess()
: BaseProcess() {
}
EmbedTexturesProcess::~EmbedTexturesProcess() {
}
bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_EmbedTextures) != 0;
}
void EmbedTexturesProcess::SetupProperties(const Importer* pImp) {
mRootPath = pImp->GetPropertyString("sourceFilePath");
mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u);
}
void EmbedTexturesProcess::Execute(aiScene* pScene) {
if (pScene == nullptr || pScene->mRootNode == nullptr) return;
aiString path;
uint32_t embeddedTexturesCount = 0u;
for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) {
auto material = pScene->mMaterials[matId];
for (auto ttId = 1u; ttId < AI_TEXTURE_TYPE_MAX; ++ttId) {
auto tt = static_cast<aiTextureType>(ttId);
auto texturesCount = material->GetTextureCount(tt);
for (auto texId = 0u; texId < texturesCount; ++texId) {
material->GetTexture(tt, texId, &path);
if (path.data[0] == '*') continue; // Already embedded
// Indeed embed
if (addTexture(pScene, path.data)) {
auto embeddedTextureId = pScene->mNumTextures - 1u;
::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId);
material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId));
embeddedTexturesCount++;
}
}
}
}
ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
}
bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
std::streampos imageSize = 0;
std::string imagePath = path;
// Test path directly
std::ifstream file(imagePath, std::ios::binary | std::ios::ate);
if ((imageSize = file.tellg()) == std::streampos(-1)) {
ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
// Test path in root path
imagePath = mRootPath + path;
file.open(imagePath, std::ios::binary | std::ios::ate);
if ((imageSize = file.tellg()) == std::streampos(-1)) {
// Test path basename in root path
imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
file.open(imagePath, std::ios::binary | std::ios::ate);
if ((imageSize = file.tellg()) == std::streampos(-1)) {
ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
return false;
}
}
}
aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
file.seekg(0, std::ios::beg);
file.read(reinterpret_cast<char*>(imageContent), imageSize);
// Enlarging the textures table
unsigned int textureId = pScene->mNumTextures++;
auto oldTextures = pScene->mTextures;
pScene->mTextures = new aiTexture*[pScene->mNumTextures];
::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u));
// Add the new texture
auto pTexture = new aiTexture;
pTexture->mHeight = 0; // Means that this is still compressed
pTexture->mWidth = static_cast<uint32_t>(imageSize);
pTexture->pcData = imageContent;
auto extension = path.substr(path.find_last_of('.') + 1u);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
if (extension == "jpeg") {
extension = "jpg";
}
size_t len = extension.size();
if (len > HINTMAXTEXTURELEN -1 ) {
len = HINTMAXTEXTURELEN - 1;
}
::strncpy(pTexture->achFormatHint, extension.c_str(), len);
pScene->mTextures[textureId] = pTexture;
return true;
}

View File

@ -0,0 +1,85 @@
/*
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.
----------------------------------------------------------------------
*/
#pragma once
#include "BaseProcess.h"
#include <string>
struct aiNode;
namespace Assimp {
/**
* Force embedding of textures (using the path = "*1" convention).
* If a texture's file does not exist at the specified path
* (due, for instance, to an absolute path generated on another system),
* it will check if a file with the same name exists at the root folder
* of the imported model. And if so, it uses that.
*/
class ASSIMP_API EmbedTexturesProcess : public BaseProcess {
public:
/// The default class constructor.
EmbedTexturesProcess();
/// The class destructor.
virtual ~EmbedTexturesProcess();
/// Overwritten, @see BaseProcess
virtual bool IsActive(unsigned int pFlags) const;
/// Overwritten, @see BaseProcess
virtual void SetupProperties(const Importer* pImp);
/// Overwritten, @see BaseProcess
virtual void Execute(aiScene* pScene);
private:
// Resolve the path and add the file content to the scene as a texture.
bool addTexture(aiScene* pScene, std::string path) const;
private:
std::string mRootPath;
};
} // namespace Assimp

648
thirdparty/assimp/code/Exporter.cpp vendored Normal file
View File

@ -0,0 +1,648 @@
/*
---------------------------------------------------------------------------
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 "DefaultProgressHandler.h"
#include "BaseProcess.h"
#include "JoinVerticesProcess.h"
#include "MakeVerboseFormat.h"
#include "ConvertToLHProcess.h"
#include "PretransformVertices.h"
#include <assimp/Exceptional.h>
#include "ScenePrivate.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* );
// ------------------------------------------------------------------------------------------------
// 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", "assbin" , &ExportSceneAssbin, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
Exporter::ExportFormatEntry( "assxml", "Assxml 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_3MF_EXPORTER
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 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, 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())) {
pimpl->mIOSystem = old;
return nullptr;
}
pimpl->blob = blobio->GetBlobChain();
pimpl->mIOSystem = old;
return pimpl->blob;
}
// ------------------------------------------------------------------------------------------------
bool IsVerboseFormat(const aiMesh* mesh) {
// avoid slow vector<bool> specialization
std::vector<unsigned int> seen(mesh->mNumVertices,0);
for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
const aiFace& f = mesh->mFaces[i];
for(unsigned int j = 0; j < f.mNumIndices; ++j) {
if(++seen[f.mIndices[j]] == 2) {
// found a duplicate index
return false;
}
}
}
return true;
}
// ------------------------------------------------------------------------------------------------
bool IsVerboseFormat(const aiScene* pScene) {
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
if(!IsVerboseFormat(pScene->mMeshes[i])) {
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------------------
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) || 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.
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties);
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

305
thirdparty/assimp/code/FBXAnimation.cpp vendored Normal file
View File

@ -0,0 +1,305 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXImporter.h"
#include "FBXDocumentUtil.h"
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/)
: Object(id, element, name)
{
const Scope& sc = GetRequiredScope(element);
const Element& KeyTime = GetRequiredElement(sc,"KeyTime");
const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat");
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);
}
// check if the key times are well-ordered
if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) {
DOMError("the keyframes are not in ascending order",&KeyTime);
}
const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"];
if(KeyAttrDataFloat) {
ParseVectorDataArray(attributes, *KeyAttrDataFloat);
}
const Element* KeyAttrFlags = sc["KeyAttrFlags"];
if(KeyAttrFlags) {
ParseVectorDataArray(flags, *KeyAttrFlags);
}
}
// ------------------------------------------------------------------------------------------------
AnimationCurve::~AnimationCurve()
{
// empty
}
// ------------------------------------------------------------------------------------------------
AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& 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 Scope& 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;
}
if(target_prop_whitelist) {
const char* const s = con->PropertyName().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) {
throw std::range_error("AnimationCurveNode target property is not in whitelist");
}
}
const Object* const ob = con->DestinationObject();
if(!ob) {
DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element);
continue;
}
// XXX support constraints as DOM class
//ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
target = ob;
if(!target) {
continue;
}
prop = con->PropertyName();
break;
}
if(!target) {
DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element);
}
props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false);
}
// ------------------------------------------------------------------------------------------------
AnimationCurveNode::~AnimationCurveNode()
{
// empty
}
// ------------------------------------------------------------------------------------------------
const AnimationCurveMap& AnimationCurveNode::Curves() const
{
if ( curves.empty() ) {
// resolve attached animation curves
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
for(const Connection* con : conns) {
// link should go for a property
if (!con->PropertyName().length()) {
continue;
}
const Object* const ob = con->SourceObject();
if(!ob) {
DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element);
continue;
}
const AnimationCurve* const anim = dynamic_cast<const AnimationCurve*>(ob);
if(!anim) {
DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element);
continue;
}
curves[con->PropertyName()] = anim;
}
}
return curves;
}
// ------------------------------------------------------------------------------------------------
AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Object(id, element, name)
, doc(doc)
{
const Scope& 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
}
// ------------------------------------------------------------------------------------------------
AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/,
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;
}
const Object* const ob = con->SourceObject();
if(!ob) {
DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element);
continue;
}
const AnimationCurveNode* const anim = dynamic_cast<const 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; // pray for NRVO
}
// ------------------------------------------------------------------------------------------------
AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Object(id, element, name)
{
const Scope& 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;
}
const Object* const ob = con->SourceObject();
if(!ob) {
DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element);
continue;
}
const AnimationLayer* const 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()
{
// empty
}
} //!FBX
} //!Assimp
#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

View File

@ -0,0 +1,466 @@
/*
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.
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXTokenizer.h"
#include "FBXUtil.h"
#include <assimp/defs.h>
#include <stdint.h>
#include <assimp/Exceptional.h>
#include <assimp/ByteSwapper.h>
namespace Assimp {
namespace FBX {
//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, unsigned int offset)
:
#ifdef DEBUG
contents(sbegin, static_cast<size_t>(send-sbegin)),
#endif
sbegin(sbegin)
, send(send)
, type(type)
, line(offset)
, column(BINARY_MARKER)
{
ai_assert(sbegin);
ai_assert(send);
// binary tokens may have zero length because they are sometimes dummies
// inserted by TokenizeBinary()
ai_assert(send >= sbegin);
}
namespace {
// ------------------------------------------------------------------------------------------------
// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX;
AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset)
{
throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
}
// ------------------------------------------------------------------------------------------------
uint32_t Offset(const char* begin, const char* cursor) {
ai_assert(begin <= cursor);
return static_cast<unsigned int>(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:
ai_assert(false);
};
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, unsigned int length)
{
ai_assert(input);
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);
const bool is64bits = version >= 7500;
const char *end = input + length;
while (cursor < end ) {
if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
break;
}
}
}
} // !FBX
} // !Assimp
#endif

86
thirdparty/assimp/code/FBXCommon.h vendored Normal file
View File

@ -0,0 +1,86 @@
/*
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 AI_FBXCOMMON_H_INC
#define AI_FBXCOMMON_H_INC
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
namespace FBX
{
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
};
// transformation inheritance method. Most of the time RSrs
enum TransformInheritance {
TransformInheritance_RrSs = 0,
TransformInheritance_RSrs,
TransformInheritance_Rrs,
TransformInheritance_MAX // end-of-enum sentinel
};
}
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // AI_FBXCOMMON_H_INC

View File

@ -0,0 +1,70 @@
/*
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 FBXCompileConfig.h
* @brief FBX importer compile-time switches
*/
#ifndef INCLUDED_AI_FBX_COMPILECONFIG_H
#define INCLUDED_AI_FBX_COMPILECONFIG_H
#include <map>
//
#if _MSC_VER > 1500 || (defined __GNUC___)
# define ASSIMP_FBX_USE_UNORDERED_MULTIMAP
# else
# define fbx_unordered_map map
# define fbx_unordered_multimap multimap
#endif
#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
# include <unordered_map>
# if _MSC_VER > 1600
# define fbx_unordered_map unordered_map
# define fbx_unordered_multimap unordered_multimap
# else
# define fbx_unordered_map tr1::unordered_map
# define fbx_unordered_multimap tr1::unordered_multimap
# endif
#endif
#endif // INCLUDED_AI_FBX_COMPILECONFIG_H

3515
thirdparty/assimp/code/FBXConverter.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

457
thirdparty/assimp/code/FBXConverter.h vendored Normal file
View File

@ -0,0 +1,457 @@
/*
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 FBXDConverter.h
* @brief FBX DOM to aiScene conversion
*/
#ifndef INCLUDED_AI_FBX_CONVERTER_H
#define INCLUDED_AI_FBX_CONVERTER_H
#include "FBXParser.h"
#include "FBXMeshGeometry.h"
#include "FBXDocument.h"
#include "FBXUtil.h"
#include "FBXProperties.h"
#include "FBXImporter.h"
#include <assimp/anim.h>
#include <assimp/material.h>
#include <assimp/light.h>
#include <assimp/texture.h>
#include <assimp/camera.h>
#include <assimp/StringComparison.h>
struct aiScene;
struct aiNode;
struct aiMaterial;
struct morphKeyData {
std::vector<unsigned int> values;
std::vector<float> weights;
};
typedef std::map<int64_t, morphKeyData*> morphAnimData;
namespace Assimp {
namespace FBX {
class Document;
using NodeNameCache = std::set<std::string>;
/**
* Convert a FBX #Document to #aiScene
* @param out Empty scene to be populated
* @param doc Parsed FBX document
*/
void ConvertToAssimpScene(aiScene* out, const Document& doc);
/** Dummy class to encapsulate the conversion process */
class FBXConverter {
public:
/**
* The different parts that make up the final local transformation of a fbx-node
*/
enum TransformationComp {
TransformationComp_GeometricScalingInverse = 0,
TransformationComp_GeometricRotationInverse,
TransformationComp_GeometricTranslationInverse,
TransformationComp_Translation,
TransformationComp_RotationOffset,
TransformationComp_RotationPivot,
TransformationComp_PreRotation,
TransformationComp_Rotation,
TransformationComp_PostRotation,
TransformationComp_RotationPivotInverse,
TransformationComp_ScalingOffset,
TransformationComp_ScalingPivot,
TransformationComp_Scaling,
TransformationComp_ScalingPivotInverse,
TransformationComp_GeometricTranslation,
TransformationComp_GeometricRotation,
TransformationComp_GeometricScaling,
TransformationComp_MAXIMUM
};
public:
FBXConverter(aiScene* out, const Document& doc);
~FBXConverter();
private:
// ------------------------------------------------------------------------------------------------
// find scene root and trigger recursive scene conversion
void ConvertRootNode();
// ------------------------------------------------------------------------------------------------
// collect and assign child nodes
void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4());
// ------------------------------------------------------------------------------------------------
void ConvertLights(const Model& model, const std::string &orig_name );
// ------------------------------------------------------------------------------------------------
void ConvertCameras(const Model& model, const std::string &orig_name );
// ------------------------------------------------------------------------------------------------
void ConvertLight( const Light& light, const std::string &orig_name );
// ------------------------------------------------------------------------------------------------
void ConvertCamera( const Camera& cam, const std::string &orig_name );
// ------------------------------------------------------------------------------------------------
void GetUniqueName( const std::string &name, std::string& uniqueName );
// ------------------------------------------------------------------------------------------------
// this returns unified names usable within assimp identifiers (i.e. no space characters -
// while these would be allowed, they are a potential trouble spot so better not use them).
const char* NameTransformationComp(TransformationComp comp);
// ------------------------------------------------------------------------------------------------
// note: this returns the REAL fbx property names
const char* NameTransformationCompProperty(TransformationComp comp);
// ------------------------------------------------------------------------------------------------
aiVector3D TransformationCompDefaultValue(TransformationComp comp);
// ------------------------------------------------------------------------------------------------
void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out);
// ------------------------------------------------------------------------------------------------
/**
* checks if a node has more than just scaling, rotation and translation components
*/
bool NeedsComplexTransformationChain(const Model& model);
// ------------------------------------------------------------------------------------------------
// note: name must be a FixNodeName() result
std::string NameTransformationChainNode(const std::string& name, TransformationComp comp);
// ------------------------------------------------------------------------------------------------
/**
* note: memory for output_nodes will be managed by the caller
*/
void GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
// ------------------------------------------------------------------------------------------------
void SetupNodeMetadata(const Model& model, aiNode& nd);
// ------------------------------------------------------------------------------------------------
void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform);
// ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd);
// ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
static_cast<unsigned int>(-1);
// ------------------------------------------------------------------------------------------------
/**
* - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into
* account when determining which weights to include.
* - outputVertStartIndices is only used when a material index is specified, it gives for
* each output vertex the DOM index it maps to.
*/
void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
unsigned int materialIndex = NO_MATERIAL_SEPARATION,
std::vector<unsigned int>* outputVertStartIndices = NULL);
// ------------------------------------------------------------------------------------------------
void ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
std::vector<size_t>& out_indices,
std::vector<size_t>& index_out_indices,
std::vector<size_t>& count_out_indices,
const aiMatrix4x4& node_global_transform);
// ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
MatIndexArray::value_type materialIndex);
// ------------------------------------------------------------------------------------------------
unsigned int GetDefaultMaterial();
// ------------------------------------------------------------------------------------------------
// Material -> aiMaterial
unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh);
// ------------------------------------------------------------------------------------------------
// Video -> aiTexture
unsigned int ConvertVideo(const Video& video);
// ------------------------------------------------------------------------------------------------
// convert embedded texture if necessary and return actual texture path
aiString GetTexturePath(const Texture* tex);
// ------------------------------------------------------------------------------------------------
void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
const std::string& propName,
aiTextureType target, const MeshGeometry* const mesh);
// ------------------------------------------------------------------------------------------------
void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures,
const std::string& propName,
aiTextureType target, const MeshGeometry* const mesh);
// ------------------------------------------------------------------------------------------------
void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh);
// ------------------------------------------------------------------------------------------------
void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh);
// ------------------------------------------------------------------------------------------------
aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName,
bool& result);
aiColor3D GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName,
const std::string& factorName, bool& result, bool useTemplate = true);
aiColor3D GetColorProperty(const PropertyTable& props, const std::string& colorName,
bool& result, bool useTemplate = true);
// ------------------------------------------------------------------------------------------------
void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props);
void SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh);
// ------------------------------------------------------------------------------------------------
// get the number of fps for a FrameRate enumerated value
static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0);
// ------------------------------------------------------------------------------------------------
// convert animation data to aiAnimation et al
void ConvertAnimations();
// ------------------------------------------------------------------------------------------------
// takes a fbx node name and returns the identifier to be used in the assimp output scene.
// the function is guaranteed to provide consistent results over multiple invocations
// UNLESS RenameNode() is called for a particular node name.
std::string FixNodeName(const std::string& name);
std::string FixAnimMeshName(const std::string& name);
typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
// XXX: better use multi_map ..
typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
// ------------------------------------------------------------------------------------------------
void ConvertAnimationStack(const AnimationStack& st);
// ------------------------------------------------------------------------------------------------
void ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node);
// ------------------------------------------------------------------------------------------------
void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
const std::string& fixed_name,
const std::vector<const AnimationCurveNode*>& curves,
const LayerMap& layer_map,
int64_t start, int64_t stop,
double& max_time,
double& min_time);
// ------------------------------------------------------------------------------------------------
bool IsRedundantAnimationData(const Model& target,
TransformationComp comp,
const std::vector<const AnimationCurveNode*>& curves);
// ------------------------------------------------------------------------------------------------
aiNodeAnim* GenerateRotationNodeAnim(const std::string& name,
const Model& target,
const std::vector<const AnimationCurveNode*>& curves,
const LayerMap& layer_map,
int64_t start, int64_t stop,
double& max_time,
double& min_time);
// ------------------------------------------------------------------------------------------------
aiNodeAnim* GenerateScalingNodeAnim(const std::string& name,
const Model& /*target*/,
const std::vector<const AnimationCurveNode*>& curves,
const LayerMap& layer_map,
int64_t start, int64_t stop,
double& max_time,
double& min_time);
// ------------------------------------------------------------------------------------------------
aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name,
const Model& /*target*/,
const std::vector<const AnimationCurveNode*>& curves,
const LayerMap& layer_map,
int64_t start, int64_t stop,
double& max_time,
double& min_time,
bool inverse = false);
// ------------------------------------------------------------------------------------------------
// generate node anim, extracting only Rotation, Scaling and Translation from the given chain
aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name,
const Model& target,
NodeMap::const_iterator chain[TransformationComp_MAXIMUM],
NodeMap::const_iterator iter_end,
const LayerMap& layer_map,
int64_t start, int64_t stop,
double& max_time,
double& min_time,
bool reverse_order = false);
// key (time), value, mapto (component index)
typedef std::tuple<std::shared_ptr<KeyTimeList>, std::shared_ptr<KeyValueList>, unsigned int > KeyFrameList;
typedef std::vector<KeyFrameList> KeyFrameListList;
// ------------------------------------------------------------------------------------------------
KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop);
// ------------------------------------------------------------------------------------------------
KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs);
// ------------------------------------------------------------------------------------------------
void InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
const aiVector3D& def_value,
double& max_time,
double& min_time);
// ------------------------------------------------------------------------------------------------
void InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
const aiVector3D& def_value,
double& maxTime,
double& minTime,
Model::RotOrder order);
// ------------------------------------------------------------------------------------------------
void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale,
aiVectorKey* out_translation,
const KeyFrameListList& scaling,
const KeyFrameListList& translation,
const KeyFrameListList& rotation,
const KeyTimeList& times,
double& maxTime,
double& minTime,
Model::RotOrder order,
const aiVector3D& def_scale,
const aiVector3D& def_translate,
const aiVector3D& def_rotation);
// ------------------------------------------------------------------------------------------------
// euler xyz -> quat
aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order);
// ------------------------------------------------------------------------------------------------
void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
int64_t start, int64_t stop,
double& maxTime,
double& minTime);
// ------------------------------------------------------------------------------------------------
void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
const LayerMap& /*layers*/,
int64_t start, int64_t stop,
double& maxTime,
double& minTime);
// ------------------------------------------------------------------------------------------------
void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
const LayerMap& /*layers*/,
int64_t start, int64_t stop,
double& maxTime,
double& minTime,
Model::RotOrder order);
void ConvertGlobalSettings();
// ------------------------------------------------------------------------------------------------
// copy generated meshes, animations, lights, cameras and textures to the output scene
void TransferDataToScene();
private:
// 0: not assigned yet, others: index is value - 1
unsigned int defaultMaterialIndex;
std::vector<aiMesh*> meshes;
std::vector<aiMaterial*> materials;
std::vector<aiAnimation*> animations;
std::vector<aiLight*> lights;
std::vector<aiCamera*> cameras;
std::vector<aiTexture*> textures;
typedef std::map<const Material*, unsigned int> MaterialMap;
MaterialMap materials_converted;
typedef std::map<const Video*, unsigned int> VideoMap;
VideoMap textures_converted;
typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
MeshMap meshes_converted;
// fixed node name -> which trafo chain components have animations?
typedef std::map<std::string, unsigned int> NodeAnimBitMap;
NodeAnimBitMap node_anim_chain_bits;
NodeNameCache mNodeNames;
double anim_fps;
aiScene* const out;
const FBX::Document& doc;
};
}
}
#endif // INCLUDED_AI_FBX_CONVERTER_H

213
thirdparty/assimp/code/FBXDeformer.cpp vendored Normal file
View File

@ -0,0 +1,213 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXMeshGeometry.h"
#include "FBXImporter.h"
#include "FBXDocumentUtil.h"
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Object(id,element,name)
{
const Scope& sc = GetRequiredScope(element);
const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2));
props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
}
// ------------------------------------------------------------------------------------------------
Deformer::~Deformer()
{
}
// ------------------------------------------------------------------------------------------------
Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id,element,doc,name)
, node()
{
const Scope& sc = GetRequiredScope(element);
const Element* const Indexes = sc["Indexes"];
const Element* const Weights = sc["Weights"];
const Element& Transform = GetRequiredElement(sc,"Transform",&element);
const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&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* const mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
if(mod) {
node = mod;
break;
}
}
if (!node) {
DOMError("failed to read target Node for Cluster",&element);
}
}
// ------------------------------------------------------------------------------------------------
Cluster::~Cluster()
{
}
// ------------------------------------------------------------------------------------------------
Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id,element,doc,name)
, accuracy( 0.0f ) {
const Scope& sc = GetRequiredScope(element);
const Element* const Link_DeformAcuracy = sc["Link_DeformAcuracy"];
if(Link_DeformAcuracy) {
accuracy = ParseTokenAsFloat(GetRequiredToken(*Link_DeformAcuracy,0));
}
// resolve assigned clusters
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
clusters.reserve(conns.size());
for(const Connection* con : conns) {
const Cluster* const cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
if(cluster) {
clusters.push_back(cluster);
continue;
}
}
}
// ------------------------------------------------------------------------------------------------
Skin::~Skin()
{
}
// ------------------------------------------------------------------------------------------------
BlendShape::BlendShape(uint64_t id, const Element& 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* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
if (bspc) {
blendShapeChannels.push_back(bspc);
continue;
}
}
}
// ------------------------------------------------------------------------------------------------
BlendShape::~BlendShape()
{
}
// ------------------------------------------------------------------------------------------------
BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Deformer(id, element, doc, name)
{
const Scope& sc = GetRequiredScope(element);
const Element* const DeformPercent = sc["DeformPercent"];
if (DeformPercent) {
percent = ParseTokenAsFloat(GetRequiredToken(*DeformPercent, 0));
}
const Element* const FullWeights = sc["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()
{
}
// ------------------------------------------------------------------------------------------------
}
}
#endif

726
thirdparty/assimp/code/FBXDocument.cpp vendored Normal file
View File

@ -0,0 +1,726 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXDocument.h"
#include "FBXMeshGeometry.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXImporter.h"
#include "FBXImportSettings.h"
#include "FBXDocumentUtil.h"
#include "FBXProperties.h"
#include <memory>
#include <functional>
#include <map>
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
: doc(doc)
, element(element)
, id(id)
, flags() {
// empty
}
// ------------------------------------------------------------------------------------------------
LazyObject::~LazyObject()
{
// empty
}
// ------------------------------------------------------------------------------------------------
const Object* LazyObject::Get(bool dieOnError)
{
if(IsBeingConstructed() || FailedToConstruct()) {
return nullptr;
}
if (object.get()) {
return object.get();
}
// if this is the root object, we return a dummy since there
// is no root object int he fbx file - it is just referenced
// with id 0.
if(id == 0L) {
object.reset(new Object(id, element, "Model::RootNode"));
return object.get();
}
const Token& key = element.KeyToken();
const TokenList& tokens = element.Tokens();
if(tokens.size() < 3) {
DOMError("expected at least 3 tokens: id, name and class tag",&element);
}
const char* err;
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;
try {
// 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());
// For debugging
//dumpObjectClassInfo( objtype, classtag );
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")) {
object.reset(new LimbNode(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 ) ) {
// FK and IK effectors are not supported
if ( strcmp( classtag.c_str(), "IKEffector" ) && strcmp( classtag.c_str(), "FKEffector" ) ) {
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));
}
// note: order matters for these two
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));
}
}
catch(std::exception& ex) {
flags &= ~BEING_CONSTRUCTED;
flags |= FAILED_TO_CONSTRUCT;
if(dieOnError || doc.Settings().strictMode) {
throw;
}
// note: the error message is already formatted, so raw logging is ok
if(!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_ERROR(ex.what());
}
return NULL;
}
if (!object.get()) {
//DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
}
flags &= ~BEING_CONSTRUCTED;
return object.get();
}
// ------------------------------------------------------------------------------------------------
Object::Object(uint64_t id, const Element& element, const std::string& name)
: element(element)
, name(name)
, id(id)
{
// empty
}
// ------------------------------------------------------------------------------------------------
Object::~Object()
{
// empty
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props)
: props(props)
, doc(doc)
{
// empty
}
// ------------------------------------------------------------------------------------------------
FileGlobalSettings::~FileGlobalSettings()
{
// empty
}
// ------------------------------------------------------------------------------------------------
Document::Document(const Parser& parser, const ImportSettings& settings)
: settings(settings)
, parser(parser)
{
// Cannot use array default initialization syntax because vc8 fails on it
for (auto &timeStamp : creationTimeStamp) {
timeStamp = 0;
}
ReadHeader();
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(ObjectMap::value_type& v : objects) {
delete v.second;
}
for(ConnectionMap::value_type& v : src_connections) {
delete v.second;
}
// |dest_connections| contain the same Connection objects as the |src_connections|
}
// ------------------------------------------------------------------------------------------------
static const unsigned int LowerSupportedVersion = 7100;
static const unsigned int UpperSupportedVersion = 7400;
void Document::ReadHeader() {
// Read ID objects from "Objects" section
const Scope& sc = parser.GetRootScope();
const Element* const ehead = sc["FBXHeaderExtension"];
if(!ehead || !ehead->Compound()) {
DOMError("no FBXHeaderExtension dictionary found");
}
const Scope& 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 ) {
DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013");
}
if(fbxVersion > UpperSupportedVersion ) {
if(Settings().strictMode) {
DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013"
" (turn off strict mode to try anyhow) ");
}
else {
DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013,"
" trying to read it nevertheless");
}
}
const Element* const ecreator = shead["Creator"];
if(ecreator) {
creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
}
const Element* const etimestamp = shead["CreationTimeStamp"];
if(etimestamp && etimestamp->Compound()) {
const Scope& 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));
}
}
// ------------------------------------------------------------------------------------------------
void Document::ReadGlobalSettings()
{
const Scope& sc = parser.GetRootScope();
const Element* const ehead = sc["GlobalSettings"];
if ( nullptr == ehead || !ehead->Compound() ) {
DOMWarning( "no GlobalSettings dictionary found" );
globals.reset(new FileGlobalSettings(*this, std::make_shared<const PropertyTable>()));
return;
}
std::shared_ptr<const PropertyTable> props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true );
//double v = PropertyGet<float>( *props.get(), std::string("UnitScaleFactor"), 1.0 );
if(!props) {
DOMError("GlobalSettings dictionary contains no property table");
}
globals.reset(new FileGlobalSettings(*this, props));
}
// ------------------------------------------------------------------------------------------------
void Document::ReadObjects()
{
// read ID objects from "Objects" section
const Scope& sc = parser.GetRootScope();
const Element* const eobjects = sc["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 Scope& sobjects = *eobjects->Compound();
for(const ElementMap::value_type& el : sobjects.Elements()) {
// extract ID
const TokenList& tok = el.second->Tokens();
if (tok.empty()) {
DOMError("expected ID after object key",el.second);
}
const char* err;
const uint64_t id = ParseTokenAsID(*tok[0], err);
if(err) {
DOMError(err,el.second);
}
// id=0 is normally implicit
if(id == 0L) {
DOMError("encountered object with implicitly defined id 0",el.second);
}
if(objects.find(id) != objects.end()) {
DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
}
objects[id] = new LazyObject(id, *el.second, *this);
// grab all animation stacks upfront since there is no listing of them
if(!strcmp(el.first.c_str(),"AnimationStack")) {
animationStacks.push_back(id);
}
}
}
// ------------------------------------------------------------------------------------------------
void Document::ReadPropertyTemplates()
{
const Scope& sc = parser.GetRootScope();
// read property templates from "Definitions" section
const Element* const edefs = sc["Definitions"];
if(!edefs || !edefs->Compound()) {
DOMWarning("no Definitions dictionary found");
return;
}
const Scope& sdefs = *edefs->Compound();
const ElementCollection otypes = sdefs.GetCollection("ObjectType");
for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
const Element& el = *(*it).second;
const Scope* sc = el.Compound();
if(!sc) {
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->GetCollection("PropertyTemplate");
for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
const Element& el = *(*it).second;
const Scope* sc = el.Compound();
if(!sc) {
DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
continue;
}
const TokenList& tok = el.Tokens();
if(tok.empty()) {
DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
continue;
}
const std::string& pname = ParseTokenAsString(*tok[0]);
const Element* Properties70 = (*sc)["Properties70"];
if(Properties70) {
std::shared_ptr<const PropertyTable> props = std::make_shared<const PropertyTable>(
*Properties70,std::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
);
templates[oname+"."+pname] = props;
}
}
}
}
// ------------------------------------------------------------------------------------------------
void Document::ReadConnections()
{
const Scope& sc = parser.GetRootScope();
// read property templates from "Definitions" section
const Element* const econns = sc["Connections"];
if(!econns || !econns->Compound()) {
DOMError("no Connections dictionary found");
}
uint64_t insertionOrder = 0l;
const Scope& sconns = *econns->Compound();
const ElementCollection conns = sconns.GetCollection("C");
for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
const Element& 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* const lazy = GetObject(id);
const AnimationStack* stack;
if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
DOMWarning("failed to read AnimationStack object");
continue;
}
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
{
ai_assert(classnames);
ai_assert( count != 0 );
ai_assert( count <= MAX_CLASSNAMES);
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) {
const Token& 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)
{
ai_assert(doc.Objects().find(src) != doc.Objects().end());
// dest may be 0 (root node)
ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
}
// ------------------------------------------------------------------------------------------------
Connection::~Connection()
{
// empty
}
// ------------------------------------------------------------------------------------------------
LazyObject& Connection::LazySourceObject() const
{
LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy);
return *lazy;
}
// ------------------------------------------------------------------------------------------------
LazyObject& Connection::LazyDestinationObject() const
{
LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy);
return *lazy;
}
// ------------------------------------------------------------------------------------------------
const Object* Connection::SourceObject() const
{
LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy);
return lazy->Get();
}
// ------------------------------------------------------------------------------------------------
const Object* Connection::DestinationObject() const
{
LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy);
return lazy->Get();
}
} // !FBX
} // !Assimp
#endif

1180
thirdparty/assimp/code/FBXDocument.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,135 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXUtil.h"
#include "FBXDocumentUtil.h"
#include "FBXProperties.h"
namespace Assimp {
namespace FBX {
namespace Util {
// ------------------------------------------------------------------------------------------------
// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
void DOMError(const std::string& message, const Token& token)
{
throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token));
}
// ------------------------------------------------------------------------------------------------
void DOMError(const std::string& message, const Element* element /*= NULL*/)
{
if(element) {
DOMError(message,element->KeyToken());
}
throw DeadlyImportError("FBX-DOM " + message);
}
// ------------------------------------------------------------------------------------------------
// print warning, do return
void DOMWarning(const std::string& message, const Token& token)
{
if(DefaultLogger::get()) {
ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token));
}
}
// ------------------------------------------------------------------------------------------------
void DOMWarning(const std::string& message, const Element* element /*= NULL*/)
{
if(element) {
DOMWarning(message,element->KeyToken());
return;
}
if(DefaultLogger::get()) {
ASSIMP_LOG_WARN("FBX-DOM: " + message);
}
}
// ------------------------------------------------------------------------------------------------
// fetch a property table and the corresponding property template
std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
const std::string& templateName,
const Element &element,
const Scope& sc,
bool no_warn /*= false*/)
{
const Element* const Properties70 = sc["Properties70"];
std::shared_ptr<const PropertyTable> templateProps = std::shared_ptr<const PropertyTable>(
static_cast<const PropertyTable*>(NULL));
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 std::make_shared<const PropertyTable>();
}
}
return std::make_shared<const PropertyTable>(*Properties70,templateProps);
}
} // !Util
} // !FBX
} // !Assimp
#endif

120
thirdparty/assimp/code/FBXDocumentUtil.h vendored Normal file
View File

@ -0,0 +1,120 @@
/*
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 INCLUDED_AI_FBX_DOCUMENT_UTIL_H
#define INCLUDED_AI_FBX_DOCUMENT_UTIL_H
#include <assimp/defs.h>
#include <string>
#include <memory>
#include "FBXDocument.h"
struct Token;
struct Element;
namespace Assimp {
namespace FBX {
namespace Util {
/* DOM/Parse error reporting - does not return */
AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX;
// does return
void DOMWarning(const std::string& message, const Token& token);
void DOMWarning(const std::string& message, const Element* element = NULL);
// fetch a property table and the corresponding property template
std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
const std::string& templateName,
const Element &element,
const Scope& sc,
bool no_warn = false);
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
const T* ProcessSimpleConnection(const Connection& con,
bool is_object_property_conn,
const char* name,
const Element& 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();
}
const Object* const ob = con.SourceObject();
if(!ob) {
DOMWarning("failed to read source object for incoming " + std::string(name) +
" link, ignoring",
&element);
return nullptr;
}
return dynamic_cast<const T*>(ob);
}
} //!Util
} //!FBX
} //!Assimp
#endif

568
thirdparty/assimp/code/FBXExportNode.cpp vendored Normal file
View File

@ -0,0 +1,568 @@
/*
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 ASSIMP_BUILD_NO_EXPORT
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
#include "FBXExportNode.h"
#include "FBXCommon.h"
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/ai_assert.h>
#include <assimp/StringUtils.h> // ai_snprintf
#include <string>
#include <ostream>
#include <sstream> // ostringstream
#include <memory> // shared_ptr
// AddP70<type> helpers... there's no usable pattern here,
// so all are defined as separate functions.
// Even "animatable" properties are often completely different
// from the standard (nonanimated) property definition,
// so they are specified with an 'A' suffix.
void FBX::Node::AddP70int(
const std::string& name, int32_t value
) {
FBX::Node n("P");
n.AddProperties(name, "int", "Integer", "", value);
AddChild(n);
}
void FBX::Node::AddP70bool(
const std::string& name, bool value
) {
FBX::Node n("P");
n.AddProperties(name, "bool", "", "", int32_t(value));
AddChild(n);
}
void FBX::Node::AddP70double(
const std::string& name, double value
) {
FBX::Node n("P");
n.AddProperties(name, "double", "Number", "", value);
AddChild(n);
}
void FBX::Node::AddP70numberA(
const std::string& name, double value
) {
FBX::Node n("P");
n.AddProperties(name, "Number", "", "A", value);
AddChild(n);
}
void FBX::Node::AddP70color(
const std::string& name, double r, double g, double b
) {
FBX::Node n("P");
n.AddProperties(name, "ColorRGB", "Color", "", r, g, b);
AddChild(n);
}
void FBX::Node::AddP70colorA(
const std::string& name, double r, double g, double b
) {
FBX::Node n("P");
n.AddProperties(name, "Color", "", "A", r, g, b);
AddChild(n);
}
void FBX::Node::AddP70vector(
const std::string& name, double x, double y, double z
) {
FBX::Node n("P");
n.AddProperties(name, "Vector3D", "Vector", "", x, y, z);
AddChild(n);
}
void FBX::Node::AddP70vectorA(
const std::string& name, double x, double y, double z
) {
FBX::Node n("P");
n.AddProperties(name, "Vector", "", "A", x, y, z);
AddChild(n);
}
void FBX::Node::AddP70string(
const std::string& name, const std::string& value
) {
FBX::Node n("P");
n.AddProperties(name, "KString", "", "", value);
AddChild(n);
}
void FBX::Node::AddP70enum(
const std::string& name, int32_t value
) {
FBX::Node n("P");
n.AddProperties(name, "enum", "", "", value);
AddChild(n);
}
void FBX::Node::AddP70time(
const std::string& name, int64_t value
) {
FBX::Node n("P");
n.AddProperties(name, "KTime", "Time", "", value);
AddChild(n);
}
// public member functions for writing nodes to stream
void FBX::Node::Dump(
std::shared_ptr<Assimp::IOStream> outfile,
bool binary, int indent
) {
if (binary) {
Assimp::StreamWriterLE outstream(outfile);
DumpBinary(outstream);
} else {
std::ostringstream ss;
DumpAscii(ss, indent);
std::string s = ss.str();
outfile->Write(s.c_str(), s.size(), 1);
}
}
void FBX::Node::Dump(
Assimp::StreamWriterLE &outstream,
bool binary, int indent
) {
if (binary) {
DumpBinary(outstream);
} else {
std::ostringstream ss;
DumpAscii(ss, indent);
outstream.PutString(ss.str());
}
}
// public member functions for low-level writing
void FBX::Node::Begin(
Assimp::StreamWriterLE &s,
bool binary, int indent
) {
if (binary) {
BeginBinary(s);
} else {
// assume we're at the correct place to start already
(void)indent;
std::ostringstream ss;
BeginAscii(ss, indent);
s.PutString(ss.str());
}
}
void FBX::Node::DumpProperties(
Assimp::StreamWriterLE& s,
bool binary, int indent
) {
if (binary) {
DumpPropertiesBinary(s);
} else {
std::ostringstream ss;
DumpPropertiesAscii(ss, indent);
s.PutString(ss.str());
}
}
void FBX::Node::EndProperties(
Assimp::StreamWriterLE &s,
bool binary, int indent
) {
EndProperties(s, binary, indent, properties.size());
}
void FBX::Node::EndProperties(
Assimp::StreamWriterLE &s,
bool binary, int indent,
size_t num_properties
) {
if (binary) {
EndPropertiesBinary(s, num_properties);
} else {
// nothing to do
(void)indent;
}
}
void FBX::Node::BeginChildren(
Assimp::StreamWriterLE &s,
bool binary, int indent
) {
if (binary) {
// nothing to do
} else {
std::ostringstream ss;
BeginChildrenAscii(ss, indent);
s.PutString(ss.str());
}
}
void FBX::Node::DumpChildren(
Assimp::StreamWriterLE& s,
bool binary, int indent
) {
if (binary) {
DumpChildrenBinary(s);
} else {
std::ostringstream ss;
DumpChildrenAscii(ss, indent);
s.PutString(ss.str());
}
}
void FBX::Node::End(
Assimp::StreamWriterLE &s,
bool binary, int indent,
bool has_children
) {
if (binary) {
EndBinary(s, has_children);
} else {
std::ostringstream ss;
EndAscii(ss, indent, has_children);
s.PutString(ss.str());
}
}
// public member functions for writing to binary fbx
void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s)
{
// write header section (with placeholders for some things)
BeginBinary(s);
// write properties
DumpPropertiesBinary(s);
// go back and fill in property related placeholders
EndPropertiesBinary(s, properties.size());
// write children
DumpChildrenBinary(s);
// finish, filling in end offset placeholder
EndBinary(s, force_has_children || !children.empty());
}
// public member functions for writing to ascii fbx
void FBX::Node::DumpAscii(std::ostream &s, int indent)
{
// write name
BeginAscii(s, indent);
// write properties
DumpPropertiesAscii(s, indent);
if (force_has_children || !children.empty()) {
// begin children (with a '{')
BeginChildrenAscii(s, indent + 1);
// write children
DumpChildrenAscii(s, indent + 1);
}
// finish (also closing the children bracket '}')
EndAscii(s, indent, force_has_children || !children.empty());
}
// private member functions for low-level writing to fbx
void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s)
{
// remember start pos so we can come back and write the end pos
this->start_pos = s.Tell();
// placeholders for end pos and property section info
s.PutU4(0); // end pos
s.PutU4(0); // number of properties
s.PutU4(0); // total property section length
// node name
s.PutU1(uint8_t(name.size())); // length of node name
s.PutString(name); // node name as raw bytes
// property data comes after here
this->property_start = s.Tell();
}
void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s)
{
for (auto &p : properties) {
p.DumpBinary(s);
}
}
void FBX::Node::EndPropertiesBinary(
Assimp::StreamWriterLE &s,
size_t num_properties
) {
if (num_properties == 0) { return; }
size_t pos = s.Tell();
ai_assert(pos > property_start);
size_t property_section_size = pos - property_start;
s.Seek(start_pos + 4);
s.PutU4(uint32_t(num_properties));
s.PutU4(uint32_t(property_section_size));
s.Seek(pos);
}
void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s)
{
for (FBX::Node& child : children) {
child.DumpBinary(s);
}
}
void FBX::Node::EndBinary(
Assimp::StreamWriterLE &s,
bool has_children
) {
// if there were children, add a null record
if (has_children) { s.PutString(FBX::NULL_RECORD); }
// now go back and write initial pos
this->end_pos = s.Tell();
s.Seek(start_pos);
s.PutU4(uint32_t(end_pos));
s.Seek(end_pos);
}
void FBX::Node::BeginAscii(std::ostream& s, int indent)
{
s << '\n';
for (int i = 0; i < indent; ++i) { s << '\t'; }
s << name << ": ";
}
void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent)
{
for (size_t i = 0; i < properties.size(); ++i) {
if (i > 0) { s << ", "; }
properties[i].DumpAscii(s, indent);
}
}
void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent)
{
// only call this if there are actually children
s << " {";
(void)indent;
}
void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent)
{
// children will need a lot of padding and corralling
if (children.size() || force_has_children) {
for (size_t i = 0; i < children.size(); ++i) {
// no compression in ascii files, so skip this node if it exists
if (children[i].name == "EncryptionType") { continue; }
// the child can dump itself
children[i].DumpAscii(s, indent);
}
}
}
void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
{
if (!has_children) { return; } // nothing to do
s << '\n';
for (int i = 0; i < indent; ++i) { s << '\t'; }
s << "}";
}
// private helpers for static member functions
// ascii property node from vector of doubles
void FBX::Node::WritePropertyNodeAscii(
const std::string& name,
const std::vector<double>& v,
Assimp::StreamWriterLE& s,
int indent
){
char buffer[32];
FBX::Node node(name);
node.Begin(s, false, indent);
std::string vsize = to_string(v.size());
// *<size> {
s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
// indent + 1
for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
// a: value,value,value,...
s.PutString("a: ");
int count = 0;
for (size_t i = 0; i < v.size(); ++i) {
if (i > 0) { s.PutChar(','); }
int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]);
count += len;
if (count > 2048) { s.PutChar('\n'); count = 0; }
if (len < 0 || len > 31) {
// this should never happen
throw DeadlyExportError("failed to convert double to string");
}
for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
}
// }
s.PutChar('\n');
for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
s.PutChar('}'); s.PutChar(' ');
node.End(s, false, indent, false);
}
// ascii property node from vector of int32_t
void FBX::Node::WritePropertyNodeAscii(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s,
int indent
){
char buffer[32];
FBX::Node node(name);
node.Begin(s, false, indent);
std::string vsize = to_string(v.size());
// *<size> {
s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
// indent + 1
for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
// a: value,value,value,...
s.PutString("a: ");
int count = 0;
for (size_t i = 0; i < v.size(); ++i) {
if (i > 0) { s.PutChar(','); }
int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]);
count += len;
if (count > 2048) { s.PutChar('\n'); count = 0; }
if (len < 0 || len > 31) {
// this should never happen
throw DeadlyExportError("failed to convert double to string");
}
for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
}
// }
s.PutChar('\n');
for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
s.PutChar('}'); s.PutChar(' ');
node.End(s, false, indent, false);
}
// binary property node from vector of doubles
// TODO: optional zip compression!
void FBX::Node::WritePropertyNodeBinary(
const std::string& name,
const std::vector<double>& v,
Assimp::StreamWriterLE& s
){
FBX::Node node(name);
node.BeginBinary(s);
s.PutU1('d');
s.PutU4(uint32_t(v.size())); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed)
s.PutU4(uint32_t(v.size()) * 8); // data size
for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); }
node.EndPropertiesBinary(s, 1);
node.EndBinary(s, false);
}
// binary property node from vector of int32_t
// TODO: optional zip compression!
void FBX::Node::WritePropertyNodeBinary(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s
){
FBX::Node node(name);
node.BeginBinary(s);
s.PutU1('i');
s.PutU4(uint32_t(v.size())); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed)
s.PutU4(uint32_t(v.size()) * 4); // data size
for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); }
node.EndPropertiesBinary(s, 1);
node.EndBinary(s, false);
}
// public static member functions
// convenience function to create and write a property node,
// holding a single property which is an array of values.
// does not copy the data, so is efficient for large arrays.
void FBX::Node::WritePropertyNode(
const std::string& name,
const std::vector<double>& v,
Assimp::StreamWriterLE& s,
bool binary, int indent
){
if (binary) {
FBX::Node::WritePropertyNodeBinary(name, v, s);
} else {
FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
}
}
// convenience function to create and write a property node,
// holding a single property which is an array of values.
// does not copy the data, so is efficient for large arrays.
void FBX::Node::WritePropertyNode(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s,
bool binary, int indent
){
if (binary) {
FBX::Node::WritePropertyNodeBinary(name, v, s);
} else {
FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
}
}
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

271
thirdparty/assimp/code/FBXExportNode.h vendored Normal file
View File

@ -0,0 +1,271 @@
/*
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 FBXExportNode.h
* Declares the FBX::Node helper class for fbx export.
*/
#ifndef AI_FBXEXPORTNODE_H_INC
#define AI_FBXEXPORTNODE_H_INC
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
#include "FBXExportProperty.h"
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <string>
#include <vector>
namespace FBX {
class Node;
}
class FBX::Node
{
public: // public data members
// TODO: accessors
std::string name; // node name
std::vector<FBX::Property> properties; // node properties
std::vector<FBX::Node> children; // child nodes
// some nodes always pretend they have children...
bool force_has_children = false;
public: // constructors
/// The default class constructor.
Node() = default;
/// The class constructor with the name.
Node(const std::string& n)
: name(n)
, properties()
, children()
, force_has_children( false ) {
// empty
}
// convenience template to construct with properties directly
template <typename... More>
Node(const std::string& n, const More... more)
: name(n)
, properties()
, children()
, force_has_children(false) {
AddProperties(more...);
}
public: // functions to add properties or children
// add a single property to the node
template <typename T>
void AddProperty(T value) {
properties.emplace_back(value);
}
// convenience function to add multiple properties at once
template <typename T, typename... More>
void AddProperties(T value, More... more) {
properties.emplace_back(value);
AddProperties(more...);
}
void AddProperties() {}
// add a child node directly
void AddChild(const Node& node) { children.push_back(node); }
// convenience function to add a child node with a single property
template <typename... More>
void AddChild(
const std::string& name,
More... more
) {
FBX::Node c(name);
c.AddProperties(more...);
children.push_back(c);
}
public: // support specifically for dealing with Properties70 nodes
// it really is simpler to make these all separate functions.
// the versions with 'A' suffixes are for animatable properties.
// those often follow a completely different format internally in FBX.
void AddP70int(const std::string& name, int32_t value);
void AddP70bool(const std::string& name, bool value);
void AddP70double(const std::string& name, double value);
void AddP70numberA(const std::string& name, double value);
void AddP70color(const std::string& name, double r, double g, double b);
void AddP70colorA(const std::string& name, double r, double g, double b);
void AddP70vector(const std::string& name, double x, double y, double z);
void AddP70vectorA(const std::string& name, double x, double y, double z);
void AddP70string(const std::string& name, const std::string& value);
void AddP70enum(const std::string& name, int32_t value);
void AddP70time(const std::string& name, int64_t value);
// template for custom P70 nodes.
// anything that doesn't fit in the above can be created manually.
template <typename... More>
void AddP70(
const std::string& name,
const std::string& type,
const std::string& type2,
const std::string& flags,
More... more
) {
Node n("P");
n.AddProperties(name, type, type2, flags, more...);
AddChild(n);
}
public: // member functions for writing data to a file or stream
// write the full node to the given file or stream
void Dump(
std::shared_ptr<Assimp::IOStream> outfile,
bool binary, int indent
);
void Dump(Assimp::StreamWriterLE &s, bool binary, int indent);
// these other functions are for writing data piece by piece.
// they must be used carefully.
// for usage examples see FBXExporter.cpp.
void Begin(Assimp::StreamWriterLE &s, bool binary, int indent);
void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent);
void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent);
void EndProperties(
Assimp::StreamWriterLE &s, bool binary, int indent,
size_t num_properties
);
void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent);
void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent);
void End(
Assimp::StreamWriterLE &s, bool binary, int indent,
bool has_children
);
private: // internal functions used for writing
void DumpBinary(Assimp::StreamWriterLE &s);
void DumpAscii(Assimp::StreamWriterLE &s, int indent);
void DumpAscii(std::ostream &s, int indent);
void BeginBinary(Assimp::StreamWriterLE &s);
void DumpPropertiesBinary(Assimp::StreamWriterLE& s);
void EndPropertiesBinary(Assimp::StreamWriterLE &s);
void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties);
void DumpChildrenBinary(Assimp::StreamWriterLE& s);
void EndBinary(Assimp::StreamWriterLE &s, bool has_children);
void BeginAscii(std::ostream &s, int indent);
void DumpPropertiesAscii(std::ostream &s, int indent);
void BeginChildrenAscii(std::ostream &s, int indent);
void DumpChildrenAscii(std::ostream &s, int indent);
void EndAscii(std::ostream &s, int indent, bool has_children);
private: // data used for binary dumps
size_t start_pos; // starting position in stream
size_t end_pos; // ending position in stream
size_t property_start; // starting position of property section
public: // static member functions
// convenience function to create a node with a single property,
// and write it to the stream.
template <typename T>
static void WritePropertyNode(
const std::string& name,
const T value,
Assimp::StreamWriterLE& s,
bool binary, int indent
) {
FBX::Property p(value);
FBX::Node node(name, p);
node.Dump(s, binary, indent);
}
// convenience function to create and write a property node,
// holding a single property which is an array of values.
// does not copy the data, so is efficient for large arrays.
static void WritePropertyNode(
const std::string& name,
const std::vector<double>& v,
Assimp::StreamWriterLE& s,
bool binary, int indent
);
// convenience function to create and write a property node,
// holding a single property which is an array of values.
// does not copy the data, so is efficient for large arrays.
static void WritePropertyNode(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s,
bool binary, int indent
);
private: // static helper functions
static void WritePropertyNodeAscii(
const std::string& name,
const std::vector<double>& v,
Assimp::StreamWriterLE& s,
int indent
);
static void WritePropertyNodeAscii(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s,
int indent
);
static void WritePropertyNodeBinary(
const std::string& name,
const std::vector<double>& v,
Assimp::StreamWriterLE& s
);
static void WritePropertyNodeBinary(
const std::string& name,
const std::vector<int32_t>& v,
Assimp::StreamWriterLE& s
);
};
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // AI_FBXEXPORTNODE_H_INC

View File

@ -0,0 +1,364 @@
/*
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 ASSIMP_BUILD_NO_EXPORT
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
#include "FBXExportProperty.h"
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <string>
#include <vector>
#include <ostream>
#include <locale>
#include <sstream> // ostringstream
// constructors for single element properties
FBX::Property::Property(bool v)
: type('C'), data(1)
{
data = {uint8_t(v)};
}
FBX::Property::Property(int16_t v) : type('Y'), data(2)
{
uint8_t* d = data.data();
(reinterpret_cast<int16_t*>(d))[0] = v;
}
FBX::Property::Property(int32_t v) : type('I'), data(4)
{
uint8_t* d = data.data();
(reinterpret_cast<int32_t*>(d))[0] = v;
}
FBX::Property::Property(float v) : type('F'), data(4)
{
uint8_t* d = data.data();
(reinterpret_cast<float*>(d))[0] = v;
}
FBX::Property::Property(double v) : type('D'), data(8)
{
uint8_t* d = data.data();
(reinterpret_cast<double*>(d))[0] = v;
}
FBX::Property::Property(int64_t v) : type('L'), data(8)
{
uint8_t* d = data.data();
(reinterpret_cast<int64_t*>(d))[0] = v;
}
// constructors for array-type properties
FBX::Property::Property(const char* c, bool raw)
: Property(std::string(c), raw)
{}
// strings can either be saved as "raw" (R) data, or "string" (S) data
FBX::Property::Property(const std::string& s, bool raw)
: type(raw ? 'R' : 'S'), data(s.size())
{
for (size_t i = 0; i < s.size(); ++i) {
data[i] = uint8_t(s[i]);
}
}
FBX::Property::Property(const std::vector<uint8_t>& r)
: type('R'), data(r)
{}
FBX::Property::Property(const std::vector<int32_t>& va)
: type('i'), data(4*va.size())
{
int32_t* d = reinterpret_cast<int32_t*>(data.data());
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
}
FBX::Property::Property(const std::vector<int64_t>& va)
: type('l'), data(8*va.size())
{
int64_t* d = reinterpret_cast<int64_t*>(data.data());
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
}
FBX::Property::Property(const std::vector<float>& va)
: type('f'), data(4*va.size())
{
float* d = reinterpret_cast<float*>(data.data());
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
}
FBX::Property::Property(const std::vector<double>& va)
: type('d'), data(8*va.size())
{
double* d = reinterpret_cast<double*>(data.data());
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
}
FBX::Property::Property(const aiMatrix4x4& vm)
: type('d'), data(8*16)
{
double* d = reinterpret_cast<double*>(data.data());
for (unsigned int c = 0; c < 4; ++c) {
for (unsigned int r = 0; r < 4; ++r) {
d[4*c+r] = vm[r][c];
}
}
}
// public member functions
size_t FBX::Property::size()
{
switch (type) {
case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L':
return data.size() + 1;
case 'S': case 'R':
return data.size() + 5;
case 'i': case 'd':
return data.size() + 13;
default:
throw DeadlyExportError("Requested size on property of unknown type");
}
}
void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s)
{
s.PutU1(type);
uint8_t* d = data.data();
size_t N;
switch (type) {
case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
case 'S':
case 'R':
s.PutU4(uint32_t(data.size()));
for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
return;
case 'i':
N = data.size() / 4;
s.PutU4(uint32_t(N)); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size
for (size_t i = 0; i < N; ++i) {
s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
}
return;
case 'l':
N = data.size() / 8;
s.PutU4(uint32_t(N)); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size
for (size_t i = 0; i < N; ++i) {
s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
}
return;
case 'f':
N = data.size() / 4;
s.PutU4(uint32_t(N)); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size
for (size_t i = 0; i < N; ++i) {
s.PutF4((reinterpret_cast<float*>(d))[i]);
}
return;
case 'd':
N = data.size() / 8;
s.PutU4(uint32_t(N)); // number of elements
s.PutU4(0); // no encoding (1 would be zip-compressed)
// TODO: compress if large?
s.PutU4(uint32_t(data.size())); // data size
for (size_t i = 0; i < N; ++i) {
s.PutF8((reinterpret_cast<double*>(d))[i]);
}
return;
default:
std::ostringstream err;
err << "Tried to dump property with invalid type '";
err << type << "'!";
throw DeadlyExportError(err.str());
}
}
void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
{
std::ostringstream ss;
ss.imbue(std::locale::classic());
ss.precision(15); // this seems to match official FBX SDK exports
DumpAscii(ss, indent);
outstream.PutString(ss.str());
}
void FBX::Property::DumpAscii(std::ostream& s, int indent)
{
// no writing type... or anything. just shove it into the stream.
uint8_t* d = data.data();
size_t N;
size_t swap = data.size();
size_t count = 0;
switch (type) {
case 'C':
if (*(reinterpret_cast<uint8_t*>(d))) { s << 'T'; }
else { s << 'F'; }
return;
case 'Y': s << *(reinterpret_cast<int16_t*>(d)); return;
case 'I': s << *(reinterpret_cast<int32_t*>(d)); return;
case 'F': s << *(reinterpret_cast<float*>(d)); return;
case 'D': s << *(reinterpret_cast<double*>(d)); return;
case 'L': s << *(reinterpret_cast<int64_t*>(d)); return;
case 'S':
// first search to see if it has "\x00\x01" in it -
// which separates fields which are reversed in the ascii version.
// yeah.
// FBX, yeah.
for (size_t i = 0; i < data.size(); ++i) {
if (data[i] == '\0') {
swap = i;
break;
}
}
case 'R':
s << '"';
// we might as well check this now,
// probably it will never happen
for (size_t i = 0; i < data.size(); ++i) {
char c = data[i];
if (c == '"') {
throw runtime_error("can't handle quotes in property string");
}
}
// first write the SWAPPED member (if any)
for (size_t i = swap + 2; i < data.size(); ++i) {
char c = data[i];
s << c;
}
// then a separator
if (swap != data.size()) {
s << "::";
}
// then the initial member
for (size_t i = 0; i < swap; ++i) {
char c = data[i];
s << c;
}
s << '"';
return;
case 'i':
N = data.size() / 4; // number of elements
s << '*' << N << " {\n";
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
s << "a: ";
for (size_t i = 0; i < N; ++i) {
if (i > 0) { s << ','; }
if (count++ > 120) { s << '\n'; count = 0; }
s << (reinterpret_cast<int32_t*>(d))[i];
}
s << '\n';
for (int i = 0; i < indent; ++i) { s << '\t'; }
s << "} ";
return;
case 'l':
N = data.size() / 8;
s << '*' << N << " {\n";
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
s << "a: ";
for (size_t i = 0; i < N; ++i) {
if (i > 0) { s << ','; }
if (count++ > 120) { s << '\n'; count = 0; }
s << (reinterpret_cast<int64_t*>(d))[i];
}
s << '\n';
for (int i = 0; i < indent; ++i) { s << '\t'; }
s << "} ";
return;
case 'f':
N = data.size() / 4;
s << '*' << N << " {\n";
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
s << "a: ";
for (size_t i = 0; i < N; ++i) {
if (i > 0) { s << ','; }
if (count++ > 120) { s << '\n'; count = 0; }
s << (reinterpret_cast<float*>(d))[i];
}
s << '\n';
for (int i = 0; i < indent; ++i) { s << '\t'; }
s << "} ";
return;
case 'd':
N = data.size() / 8;
s << '*' << N << " {\n";
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
s << "a: ";
// set precision to something that can handle doubles
s.precision(15);
for (size_t i = 0; i < N; ++i) {
if (i > 0) { s << ','; }
if (count++ > 120) { s << '\n'; count = 0; }
s << (reinterpret_cast<double*>(d))[i];
}
s << '\n';
for (int i = 0; i < indent; ++i) { s << '\t'; }
s << "} ";
return;
default:
std::ostringstream err;
err << "Tried to dump property with invalid type '";
err << type << "'!";
throw runtime_error(err.str());
}
}
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -0,0 +1,129 @@
/*
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 FBXExportProperty.h
* Declares the FBX::Property helper class for fbx export.
*/
#ifndef AI_FBXEXPORTPROPERTY_H_INC
#define AI_FBXEXPORTPROPERTY_H_INC
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
#include <assimp/types.h> // aiMatrix4x4
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <string>
#include <vector>
#include <ostream>
#include <type_traits> // is_void
namespace FBX {
class Property;
}
/** FBX::Property
*
* Holds a value of any of FBX's recognized types,
* each represented by a particular one-character code.
* C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true
* Y : 2-byte int16
* I : 4-byte int32
* F : 4-byte float
* D : 8-byte double
* L : 8-byte int64
* i : array of int32
* f : array of float
* d : array of double
* l : array of int64
* b : array of 1-byte booleans (0x00 or 0x01)
* S : string (array of 1-byte char)
* R : raw data (array of bytes)
*/
class FBX::Property
{
public:
// constructors for basic types.
// all explicit to avoid accidental typecasting
explicit Property(bool v);
// TODO: determine if there is actually a byte type,
// or if this always means <bool>. 'C' seems to imply <char>,
// so possibly the above was intended to represent both.
explicit Property(int16_t v);
explicit Property(int32_t v);
explicit Property(float v);
explicit Property(double v);
explicit Property(int64_t v);
// strings can either be stored as 'R' (raw) or 'S' (string) type
explicit Property(const char* c, bool raw=false);
explicit Property(const std::string& s, bool raw=false);
explicit Property(const std::vector<uint8_t>& r);
explicit Property(const std::vector<int32_t>& va);
explicit Property(const std::vector<int64_t>& va);
explicit Property(const std::vector<double>& va);
explicit Property(const std::vector<float>& va);
explicit Property(const aiMatrix4x4& vm);
// this will catch any type not defined above,
// so that we don't accidentally convert something we don't want.
// for example (const char*) --> (bool)... seriously wtf C++
template <class T>
explicit Property(T v) : type('X') {
static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
} // note: no line wrap so it appears verbatim on the compiler error
// the size of this property node in a binary file, in bytes
size_t size();
// write this property node as binary data to the given stream
void DumpBinary(Assimp::StreamWriterLE &s);
void DumpAscii(Assimp::StreamWriterLE &s, int indent=0);
void DumpAscii(std::ostream &s, int indent=0);
// note: make sure the ostream is in classic "C" locale
private:
char type;
std::vector<uint8_t> data;
};
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // AI_FBXEXPORTPROPERTY_H_INC

2480
thirdparty/assimp/code/FBXExporter.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

178
thirdparty/assimp/code/FBXExporter.h vendored Normal file
View File

@ -0,0 +1,178 @@
/*
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 FBXExporter.h
* Declares the exporter class to write a scene to an fbx file
*/
#ifndef AI_FBXEXPORTER_H_INC
#define AI_FBXEXPORTER_H_INC
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
#include "FBXExportNode.h" // FBX::Node
#include "FBXCommon.h" // FBX::TransformInheritance
#include <assimp/types.h>
//#include <assimp/material.h>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <vector>
#include <map>
#include <unordered_set>
#include <memory> // shared_ptr
#include <sstream> // stringstream
struct aiScene;
struct aiNode;
//struct aiMaterial;
namespace Assimp
{
class IOSystem;
class IOStream;
class ExportProperties;
// ---------------------------------------------------------------------
/** Helper class to export a given scene to an FBX file. */
// ---------------------------------------------------------------------
class FBXExporter
{
public:
/// Constructor for a specific scene to export
FBXExporter(const aiScene* pScene, const ExportProperties* pProperties);
// call one of these methods to export
void ExportBinary(const char* pFile, IOSystem* pIOSystem);
void ExportAscii(const char* pFile, IOSystem* pIOSystem);
private:
bool binary; // whether current export is in binary or ascii format
const aiScene* mScene; // the scene to export
const ExportProperties* mProperties; // currently unused
std::shared_ptr<IOStream> outfile; // file to write to
std::vector<FBX::Node> connections; // connection storage
std::vector<int64_t> mesh_uids;
std::vector<int64_t> material_uids;
std::map<const aiNode*,int64_t> node_uids;
// this crude unique-ID system is actually fine
int64_t last_uid = 999999;
int64_t generate_uid() { return ++last_uid; }
// binary files have a specific header and footer,
// in addition to the actual data
void WriteBinaryHeader();
void WriteBinaryFooter();
// ascii files have a comment at the top
void WriteAsciiHeader();
// WriteAllNodes does the actual export.
// It just calls all the Write<Section> methods below in order.
void WriteAllNodes();
// Methods to write individual sections.
// The order here matches the order inside an FBX file.
// Each method corresponds to a top-level FBX section,
// except WriteHeader which also includes some binary-only sections
// and WriteFooter which is binary data only.
void WriteHeaderExtension();
// WriteFileId(); // binary-only, included in WriteHeader
// WriteCreationTime(); // binary-only, included in WriteHeader
// WriteCreator(); // binary-only, included in WriteHeader
void WriteGlobalSettings();
void WriteDocuments();
void WriteReferences();
void WriteDefinitions();
void WriteObjects();
void WriteConnections();
// WriteTakes(); // deprecated since at least 2015 (fbx 7.4)
// helpers
void WriteAsciiSectionHeader(const std::string& title);
void WriteModelNodes(
Assimp::StreamWriterLE& s,
const aiNode* node,
int64_t parent_uid,
const std::unordered_set<const aiNode*>& limbnodes
);
void WriteModelNodes( // usually don't call this directly
StreamWriterLE& s,
const aiNode* node,
int64_t parent_uid,
const std::unordered_set<const aiNode*>& limbnodes,
std::vector<std::pair<std::string,aiVector3D>>& transform_chain
);
void WriteModelNode( // nor this
StreamWriterLE& s,
bool binary,
const aiNode* node,
int64_t node_uid,
const std::string& type,
const std::vector<std::pair<std::string,aiVector3D>>& xfm_chain,
FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs
);
void WriteAnimationCurveNode(
StreamWriterLE& outstream,
int64_t uid,
std::string name, // "T", "R", or "S"
aiVector3D default_value,
std::string property_name, // "Lcl Translation" etc
int64_t animation_layer_uid,
int64_t node_uid
);
void WriteAnimationCurve(
StreamWriterLE& outstream,
double default_value,
const std::vector<int64_t>& times,
const std::vector<float>& values,
int64_t curvenode_id,
const std::string& property_link // "d|X", "d|Y", etc
);
};
}
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
#endif // AI_FBXEXPORTER_H_INC

View File

@ -0,0 +1,153 @@
/*
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 INCLUDED_AI_FBX_IMPORTSETTINGS_H
#define INCLUDED_AI_FBX_IMPORTSETTINGS_H
namespace Assimp {
namespace FBX {
/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */
struct ImportSettings
{
ImportSettings()
: strictMode(true)
, readAllLayers(true)
, readAllMaterials(false)
, readMaterials(true)
, readTextures(true)
, readCameras(true)
, readLights(true)
, readAnimations(true)
, readWeights(true)
, preservePivots(true)
, optimizeEmptyAnimationCurves(true)
, useLegacyEmbeddedTextureNaming(false)
{}
/** 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;
};
} // !FBX
} // !Assimp
#endif

197
thirdparty/assimp/code/FBXImporter.cpp vendored Normal file
View File

@ -0,0 +1,197 @@
/*
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.
r
* 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 FBXImporter.cpp
* @brief Implementation of the FBX importer.
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXImporter.h"
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXDocument.h"
#include "FBXConverter.h"
#include <assimp/StreamReader.h>
#include <assimp/MemoryIOWrapper.h>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
namespace Assimp {
template<>
const char* LogFunctions<FBXImporter>::Prefix() {
static auto prefix = "FBX: ";
return prefix;
}
}
using namespace Assimp;
using namespace Assimp::Formatter;
using namespace Assimp::FBX;
namespace {
static const aiImporterDesc desc = {
"Autodesk FBX Importer",
"",
"",
"",
aiImporterFlags_SupportTextFlavour,
0,
0,
0,
0,
"fbx"
};
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer
FBXImporter::FBXImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FBXImporter::~FBXImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string& extension = GetExtension(pFile);
if (extension == std::string( desc.mFileExtensions ) ) {
return true;
}
else if ((!extension.length() || checkSig) && pIOHandler) {
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head
const char* tokens[] = {"fbx"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
// ------------------------------------------------------------------------------------------------
// List all extensions handled by this loader
const aiImporterDesc* FBXImporter::GetInfo () const
{
return &desc;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader
void FBXImporter::SetupProperties(const Importer* pImp)
{
settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
if (!stream) {
ThrowException("Could not open file for reading");
}
// read entire file into memory - no streaming for this, fbx
// files can grow large, but the assimp output data structure
// then becomes very large, too. Assimp doesn't support
// streaming for its output data structures so the net win with
// streaming input data would be very low.
std::vector<char> contents;
contents.resize(stream->FileSize()+1);
stream->Read( &*contents.begin(), 1, contents.size()-1 );
contents[ contents.size() - 1 ] = 0;
const char* const begin = &*contents.begin();
// broadphase tokenizing pass in which we identify the core
// syntax elements of FBX (brackets, commas, key:value mappings)
TokenList tokens;
try {
bool is_binary = false;
if (!strncmp(begin,"Kaydara FBX Binary",18)) {
is_binary = true;
TokenizeBinary(tokens,begin,static_cast<unsigned int>(contents.size()));
}
else {
Tokenize(tokens,begin);
}
// use this information to construct a very rudimentary
// parse-tree representing the FBX scope structure
Parser parser(tokens, is_binary);
// take the raw parse-tree and convert it to a FBX DOM
Document doc(parser,settings);
// convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene,doc);
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
}
catch(std::exception&) {
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
throw;
}
}
#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER

100
thirdparty/assimp/code/FBXImporter.h vendored Normal file
View File

@ -0,0 +1,100 @@
/*
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 FBXImporter.h
* @brief Declaration of the FBX main importer class
*/
#ifndef INCLUDED_AI_FBX_IMPORTER_H
#define INCLUDED_AI_FBX_IMPORTER_H
#include <assimp/BaseImporter.h>
#include <assimp/LogAux.h>
#include "FBXImportSettings.h"
namespace Assimp {
// TinyFormatter.h
namespace Formatter {
template <typename T,typename TR, typename A> class basic_formatter;
typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
}
// -------------------------------------------------------------------------------------------
/** Load the Autodesk FBX file format.
See http://en.wikipedia.org/wiki/FBX
*/
// -------------------------------------------------------------------------------------------
class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter>
{
public:
FBXImporter();
virtual ~FBXImporter();
// --------------------
bool CanRead( const std::string& pFile,
IOSystem* pIOHandler,
bool checkSig
) const;
protected:
// --------------------
const aiImporterDesc* GetInfo () const;
// --------------------
void SetupProperties(const Importer* pImp);
// --------------------
void InternReadFile( const std::string& pFile,
aiScene* pScene,
IOSystem* pIOHandler
);
private:
FBX::ImportSettings settings;
}; // !class FBXImporter
} // end of namespace Assimp
#endif // !INCLUDED_AI_FBX_IMPORTER_H

351
thirdparty/assimp/code/FBXMaterial.cpp vendored Normal file
View File

@ -0,0 +1,351 @@
/*
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 FBXMaterial.cpp
* @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXImporter.h"
#include "FBXImportSettings.h"
#include "FBXDocumentUtil.h"
#include "FBXProperties.h"
#include <assimp/ByteSwapper.h>
#include <algorithm> // std::transform
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Object(id,element,name)
{
const Scope& sc = GetRequiredScope(element);
const Element* const ShadingModel = sc["ShadingModel"];
const Element* const MultiLayer = sc["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;
// lower-case shading because Blender (for example) writes "Phong"
std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower);
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;
}
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);
if(!tex) {
const LayeredTexture* const layeredTexture = dynamic_cast<const 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*)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()
{
}
// ------------------------------------------------------------------------------------------------
Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Object(id,element,name)
, uvScaling(1.0f,1.0f)
, media(0)
{
const Scope& sc = GetRequiredScope(element);
const Element* const Type = sc["Type"];
const Element* const FileName = sc["FileName"];
const Element* const RelativeFilename = sc["RelativeFilename"];
const Element* const ModelUVTranslation = sc["ModelUVTranslation"];
const Element* const ModelUVScaling = sc["ModelUVScaling"];
const Element* const Texture_Alpha_Source = sc["Texture_Alpha_Source"];
const Element* const Cropping = sc["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 = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,0)),
ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,1))
);
}
if(ModelUVScaling) {
uvScaling = aiVector2D(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);
// 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()
{
}
LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name)
: Object(id,element,name)
,blendMode(BlendMode_Modulate)
,alpha(1)
{
const Scope& sc = GetRequiredScope(element);
const Element* const BlendModes = sc["BlendModes"];
const Element* const Alphas = sc["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 Element& element, const Document& doc, const std::string& name)
: Object(id,element,name)
, contentLength(0)
, content(0)
{
const Scope& sc = GetRequiredScope(element);
const Element* const Type = sc["Type"];
const Element* const FileName = sc.FindElementCaseInsensitive("FileName"); //some files retain the information as "Filename", others "FileName", who knows
const Element* const RelativeFilename = sc["RelativeFilename"];
const Element* const Content = sc["Content"];
if(Type) {
type = ParseTokenAsString(GetRequiredToken(*Type,0));
}
if(FileName) {
fileName = ParseTokenAsString(GetRequiredToken(*FileName,0));
}
if(RelativeFilename) {
relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
}
if(Content) {
//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()) {
DOMWarning("video content is not binary data, ignoring", &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 (const runtime_error& runtimeError)
{
//we don't need the content data for contents that has already been loaded
ASSIMP_LOG_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;
}
}
} //!FBX
} //!Assimp
#endif

View File

@ -0,0 +1,711 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include <functional>
#include "FBXMeshGeometry.h"
#include "FBXDocument.h"
#include "FBXImporter.h"
#include "FBXImportSettings.h"
#include "FBXDocumentUtil.h"
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Geometry::Geometry(uint64_t id, const Element& 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* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
if(sk) {
skin = sk;
}
const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
if (bsp) {
blendShapes.push_back(bsp);
}
}
}
// ------------------------------------------------------------------------------------------------
Geometry::~Geometry()
{
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
return blendShapes;
}
// ------------------------------------------------------------------------------------------------
const Skin* Geometry::DeformerSkin() const {
return skin;
}
// ------------------------------------------------------------------------------------------------
MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Geometry(id, element,name, doc)
{
const Scope* sc = element.Compound();
if (!sc) {
DOMError("failed to read Geometry object (class: Mesh), no data scope found");
}
// must have Mesh elements:
const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element);
const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element);
// optional Mesh elements:
const ElementCollection& Layer = sc->GetCollection("Layer");
std::vector<aiVector3D> tempVerts;
ParseVectorDataArray(tempVerts,Vertices);
if(tempVerts.empty()) {
FBXImporter::LogWarn("encountered mesh with no vertices");
return;
}
std::vector<int> tempFaces;
ParseVectorDataArray(tempFaces,PolygonVertexIndex);
if(tempFaces.empty()) {
FBXImporter::LogWarn("encountered mesh with no faces");
return;
}
m_vertices.reserve(tempFaces.size());
m_faces.reserve(tempFaces.size() / 3);
m_mapping_offsets.resize(tempVerts.size());
m_mapping_counts.resize(tempVerts.size(),0);
m_mappings.resize(tempFaces.size());
const size_t vertex_count = tempVerts.size();
// generate output vertices, computing an adjacency table to
// preserve the mapping from fbx indices to *this* indexing.
unsigned int count = 0;
for(int index : tempFaces) {
const int absi = index < 0 ? (-index - 1) : index;
if(static_cast<size_t>(absi) >= vertex_count) {
DOMError("polygon vertex index out of range",&PolygonVertexIndex);
}
m_vertices.push_back(tempVerts[absi]);
++count;
++m_mapping_counts[absi];
if (index < 0) {
m_faces.push_back(count);
count = 0;
}
}
unsigned int cursor = 0;
for (size_t i = 0, e = tempVerts.size(); i < e; ++i) {
m_mapping_offsets[i] = cursor;
cursor += m_mapping_counts[i];
m_mapping_counts[i] = 0;
}
cursor = 0;
for(int index : tempFaces) {
const int absi = index < 0 ? (-index - 1) : index;
m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++;
}
// if settings.readAllLayers is true:
// * read all layers, try to load as many vertex channels as possible
// if settings.readAllLayers is false:
// * read only the layer with index 0, but warn about any further layers
for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
const TokenList& tokens = (*it).second->Tokens();
const char* err;
const int index = ParseTokenAsInt(*tokens[0], err);
if(err) {
DOMError(err,&element);
}
if(doc.Settings().readAllLayers || index == 0) {
const Scope& layer = GetRequiredScope(*(*it).second);
ReadLayer(layer);
}
else {
FBXImporter::LogWarn("ignoring additional geometry layers");
}
}
}
// ------------------------------------------------------------------------------------------------
MeshGeometry::~MeshGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetNormals() const {
return m_normals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetTangents() const {
return m_tangents;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& MeshGeometry::GetBinormals() const {
return m_binormals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<unsigned int>& MeshGeometry::GetFaceIndexCounts() const {
return m_faces;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector2D>& MeshGeometry::GetTextureCoords( unsigned int index ) const {
static const std::vector<aiVector2D> empty;
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ];
}
std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const {
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ];
}
const std::vector<aiColor4D>& MeshGeometry::GetVertexColors( unsigned int index ) const {
static const std::vector<aiColor4D> empty;
return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ];
}
const MatIndexArray& MeshGeometry::GetMaterialIndices() const {
return m_materials;
}
// ------------------------------------------------------------------------------------------------
const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const {
if ( in_index >= m_mapping_counts.size() ) {
return NULL;
}
ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() );
count = m_mapping_counts[ in_index ];
ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() );
return &m_mappings[ m_mapping_offsets[ in_index ] ];
}
// ------------------------------------------------------------------------------------------------
unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const {
ai_assert( in_index < m_vertices.size() );
// in the current conversion pattern this will only be needed if
// weights are present, so no need to always pre-compute this table
if ( m_facesVertexStartIndices.empty() ) {
m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 );
std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 );
m_facesVertexStartIndices.pop_back();
}
ai_assert( m_facesVertexStartIndices.size() == m_faces.size() );
const std::vector<unsigned int>::iterator it = std::upper_bound(
m_facesVertexStartIndices.begin(),
m_facesVertexStartIndices.end(),
in_index
);
return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) );
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadLayer(const Scope& layer)
{
const ElementCollection& LayerElement = layer.GetCollection("LayerElement");
for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
const Scope& elayer = GetRequiredScope(*(*eit).second);
ReadLayerElement(elayer);
}
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadLayerElement(const Scope& layerElement)
{
const Element& Type = GetRequiredElement(layerElement,"Type");
const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex");
const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0));
const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0));
const Scope& top = GetRequiredScope(element);
const ElementCollection candidates = top.GetCollection(type);
for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) {
const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0));
if(index == typedIndex) {
ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second));
return;
}
}
FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ")
<< type << ", index: " << typedIndex);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source)
{
const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(source,"MappingInformationType"),0)
);
const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
GetRequiredElement(source,"ReferenceInformationType"),0)
);
if (type == "LayerElementUV") {
if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ")
<< index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" );
return;
}
const Element* Name = source["Name"];
m_uvNames[index] = "";
if(Name) {
m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
}
ReadVertexDataUV(m_uvs[index],source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementMaterial") {
if (m_materials.size() > 0) {
FBXImporter::LogError("ignoring additional material layer");
return;
}
std::vector<int> temp_materials;
ReadVertexDataMaterials(temp_materials,source,
MappingInformationType,
ReferenceInformationType
);
// sometimes, there will be only negative entries. Drop the material
// layer in such a case (I guess it means a default material should
// be used). This is what the converter would do anyway, and it
// avoids losing the material if there are more material layers
// coming of which at least one contains actual data (did observe
// that with one test file).
const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; });
if(count_neg == temp_materials.size()) {
FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)");
return;
}
std::swap(temp_materials, m_materials);
}
else if (type == "LayerElementNormal") {
if (m_normals.size() > 0) {
FBXImporter::LogError("ignoring additional normal layer");
return;
}
ReadVertexDataNormals(m_normals,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementTangent") {
if (m_tangents.size() > 0) {
FBXImporter::LogError("ignoring additional tangent layer");
return;
}
ReadVertexDataTangents(m_tangents,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementBinormal") {
if (m_binormals.size() > 0) {
FBXImporter::LogError("ignoring additional binormal layer");
return;
}
ReadVertexDataBinormals(m_binormals,source,
MappingInformationType,
ReferenceInformationType
);
}
else if (type == "LayerElementColor") {
if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) {
FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ")
<< index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" );
return;
}
ReadVertexDataColors(m_colors[index],source,
MappingInformationType,
ReferenceInformationType
);
}
}
// ------------------------------------------------------------------------------------------------
// Lengthy utility function to read and resolve a FBX vertex data array - that is, the
// output is in polygon vertex order. This logic is used for reading normals, UVs, colors,
// tangents ..
template <typename T>
void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType,
const char* dataElementName,
const char* indexDataElementName,
size_t vertex_count,
const std::vector<unsigned int>& mapping_counts,
const std::vector<unsigned int>& mapping_offsets,
const std::vector<unsigned int>& mappings)
{
bool isDirect = ReferenceInformationType == "Direct";
bool isIndexToDirect = ReferenceInformationType == "IndexToDirect";
// fall-back to direct data if there is no index data element
if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) {
isDirect = true;
isIndexToDirect = false;
}
// handle permutations of Mapping and Reference type - it would be nice to
// deal with this more elegantly and with less redundancy, but right
// now it seems unavoidable.
if (MappingInformationType == "ByVertice" && isDirect) {
if (!HasElement(source, dataElementName)) {
return;
}
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
data_out.resize(vertex_count);
for (size_t i = 0, e = tempData.size(); i < e; ++i) {
const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
for (unsigned int j = istart; j < iend; ++j) {
data_out[mappings[j]] = tempData[i];
}
}
}
else if (MappingInformationType == "ByVertice" && isIndexToDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
data_out.resize(vertex_count);
std::vector<int> uvIndices;
ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
for (size_t i = 0, e = uvIndices.size(); i < e; ++i) {
const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
for (unsigned int j = istart; j < iend; ++j) {
if (static_cast<size_t>(uvIndices[i]) >= tempData.size()) {
DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
}
data_out[mappings[j]] = tempData[uvIndices[i]];
}
}
}
else if (MappingInformationType == "ByPolygonVertex" && isDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
if (tempData.size() != vertex_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
<< tempData.size() << ", expected " << vertex_count
);
return;
}
data_out.swap(tempData);
}
else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) {
std::vector<T> tempData;
ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
data_out.resize(vertex_count);
std::vector<int> uvIndices;
ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
if (uvIndices.size() != vertex_count) {
FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping");
return;
}
const T empty;
unsigned int next = 0;
for(int i : uvIndices) {
if ( -1 == i ) {
data_out[ next++ ] = empty;
continue;
}
if (static_cast<size_t>(i) >= tempData.size()) {
DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
}
data_out[next++] = tempData[i];
}
}
else {
FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ")
<< MappingInformationType << "," << ReferenceInformationType);
}
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType,
"Normals",
"NormalsIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType,
"UV",
"UVIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType,
"Colors",
"ColorIndex",
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
static const std::string TangentIndexToken = "TangentIndex";
static const std::string TangentsIndexToken = "TangentsIndex";
void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str();
ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
static const std::string BinormalIndexToken = "BinormalIndex";
static const std::string BinormalsIndexToken = "BinormalsIndex";
void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal";
const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str();
ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
str,
strIdx,
m_vertices.size(),
m_mapping_counts,
m_mapping_offsets,
m_mappings);
}
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType)
{
const size_t face_count = m_faces.size();
ai_assert(face_count);
// materials are handled separately. First of all, they are assigned per-face
// and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
// has a slightly different meaning for materials.
ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials"));
if (MappingInformationType == "AllSame") {
// easy - same material for all faces
if (materials_out.empty()) {
FBXImporter::LogError(Formatter::format("expected material index, ignoring"));
return;
}
else if (materials_out.size() > 1) {
FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one"));
materials_out.clear();
}
m_materials.assign(m_vertices.size(),materials_out[0]);
}
else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
m_materials.resize(face_count);
if(materials_out.size() != face_count) {
FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
<< materials_out.size() << ", expected " << face_count
);
return;
}
}
else {
FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ")
<< MappingInformationType << "," << ReferenceInformationType);
}
}
// ------------------------------------------------------------------------------------------------
ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Geometry(id, element, name, doc)
{
const Scope* sc = element.Compound();
if (!sc) {
DOMError("failed to read Geometry object (class: Shape), no data scope found");
}
const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
ParseVectorDataArray(m_indices, Indexes);
ParseVectorDataArray(m_vertices, Vertices);
ParseVectorDataArray(m_normals, Normals);
}
// ------------------------------------------------------------------------------------------------
ShapeGeometry::~ShapeGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& ShapeGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& ShapeGeometry::GetNormals() const {
return m_normals;
}
// ------------------------------------------------------------------------------------------------
const std::vector<unsigned int>& ShapeGeometry::GetIndices() const {
return m_indices;
}
// ------------------------------------------------------------------------------------------------
LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
: Geometry(id, element, name, doc)
{
const Scope* sc = element.Compound();
if (!sc) {
DOMError("failed to read Geometry object (class: Line), no data scope found");
}
const Element& Points = GetRequiredElement(*sc, "Points", &element);
const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element);
ParseVectorDataArray(m_vertices, Points);
ParseVectorDataArray(m_indices, PointsIndex);
}
// ------------------------------------------------------------------------------------------------
LineGeometry::~LineGeometry() {
// empty
}
// ------------------------------------------------------------------------------------------------
const std::vector<aiVector3D>& LineGeometry::GetVertices() const {
return m_vertices;
}
// ------------------------------------------------------------------------------------------------
const std::vector<int>& LineGeometry::GetIndices() const {
return m_indices;
}
} // !FBX
} // !Assimp
#endif

235
thirdparty/assimp/code/FBXMeshGeometry.h vendored Normal file
View File

@ -0,0 +1,235 @@
/*
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 FBXImporter.h
* @brief Declaration of the FBX main importer class
*/
#ifndef INCLUDED_AI_FBX_MESHGEOMETRY_H
#define INCLUDED_AI_FBX_MESHGEOMETRY_H
#include "FBXParser.h"
#include "FBXDocument.h"
namespace Assimp {
namespace FBX {
/**
* DOM base class for all kinds of FBX geometry
*/
class Geometry : public Object
{
public:
Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
virtual ~Geometry();
/** Get the Skin attached to this geometry or NULL */
const Skin* DeformerSkin() const;
/** Get the BlendShape attached to this geometry or NULL */
const std::vector<const BlendShape*>& GetBlendShapes() const;
private:
const Skin* skin;
std::vector<const BlendShape*> blendShapes;
};
typedef std::vector<int> MatIndexArray;
/**
* DOM class for FBX geometry of type "Mesh"
*/
class MeshGeometry : public Geometry
{
public:
/** The class constructor */
MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
/** The class destructor */
virtual ~MeshGeometry();
/** Get a list of all vertex points, non-unique*/
const std::vector<aiVector3D>& GetVertices() const;
/** Get a list of all vertex normals or an empty array if
* no normals are specified. */
const std::vector<aiVector3D>& GetNormals() const;
/** Get a list of all vertex tangents or an empty array
* if no tangents are specified */
const std::vector<aiVector3D>& GetTangents() const;
/** Get a list of all vertex bi-normals or an empty array
* if no bi-normals are specified */
const std::vector<aiVector3D>& GetBinormals() const;
/** Return list of faces - each entry denotes a face and specifies
* how many vertices it has. Vertices are taken from the
* vertex data arrays in sequential order. */
const std::vector<unsigned int>& GetFaceIndexCounts() const;
/** Get a UV coordinate slot, returns an empty array if
* the requested slot does not exist. */
const std::vector<aiVector2D>& GetTextureCoords( unsigned int index ) const;
/** Get a UV coordinate slot, returns an empty array if
* the requested slot does not exist. */
std::string GetTextureCoordChannelName( unsigned int index ) const;
/** Get a vertex color coordinate slot, returns an empty array if
* the requested slot does not exist. */
const std::vector<aiColor4D>& GetVertexColors( unsigned int index ) const;
/** Get per-face-vertex material assignments */
const MatIndexArray& GetMaterialIndices() const;
/** Convert from a fbx file vertex index (for example from a #Cluster weight) or NULL
* if the vertex index is not valid. */
const unsigned int* ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const;
/** Determine the face to which a particular output vertex index belongs.
* This mapping is always unique. */
unsigned int FaceForVertexIndex( unsigned int in_index ) const;
private:
void ReadLayer( const Scope& layer );
void ReadLayerElement( const Scope& layerElement );
void ReadVertexData( const std::string& type, int index, const Scope& source );
void ReadVertexDataUV( std::vector<aiVector2D>& uv_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType );
void ReadVertexDataNormals( std::vector<aiVector3D>& normals_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType );
void ReadVertexDataColors( std::vector<aiColor4D>& colors_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType );
void ReadVertexDataTangents( std::vector<aiVector3D>& tangents_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType );
void ReadVertexDataBinormals( std::vector<aiVector3D>& binormals_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType );
void ReadVertexDataMaterials( MatIndexArray& materials_out, const Scope& source,
const std::string& MappingInformationType,
const std::string& ReferenceInformationType );
private:
// cached data arrays
MatIndexArray m_materials;
std::vector<aiVector3D> m_vertices;
std::vector<unsigned int> m_faces;
mutable std::vector<unsigned int> m_facesVertexStartIndices;
std::vector<aiVector3D> m_tangents;
std::vector<aiVector3D> m_binormals;
std::vector<aiVector3D> m_normals;
std::string m_uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
std::vector<aiVector2D> m_uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
std::vector<aiColor4D> m_colors[ AI_MAX_NUMBER_OF_COLOR_SETS ];
std::vector<unsigned int> m_mapping_counts;
std::vector<unsigned int> m_mapping_offsets;
std::vector<unsigned int> m_mappings;
};
/**
* DOM class for FBX geometry of type "Shape"
*/
class ShapeGeometry : public Geometry
{
public:
/** The class constructor */
ShapeGeometry(uint64_t id, const Element& 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<aiVector3D>& GetVertices() const;
/** Get a list of all vertex normals or an empty array if
* no normals are specified. */
const std::vector<aiVector3D>& GetNormals() const;
/** Return list of vertex indices. */
const std::vector<unsigned int>& GetIndices() const;
private:
std::vector<aiVector3D> m_vertices;
std::vector<aiVector3D> 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 Element& 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<aiVector3D>& GetVertices() const;
/** Return list of vertex indices. */
const std::vector<int>& GetIndices() const;
private:
std::vector<aiVector3D> m_vertices;
std::vector<int> m_indices;
};
}
}
#endif // INCLUDED_AI_FBX_MESHGEOMETRY_H

153
thirdparty/assimp/code/FBXModel.cpp vendored Normal file
View File

@ -0,0 +1,153 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXMeshGeometry.h"
#include "FBXDocument.h"
#include "FBXImporter.h"
#include "FBXDocumentUtil.h"
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
Model::Model(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Object(id,element,name)
, shading("Y")
{
const Scope& sc = GetRequiredScope(element);
const Element* const Shading = sc["Shading"];
const Element* const Culling = sc["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()
{
}
// ------------------------------------------------------------------------------------------------
void Model::ResolveLinks(const Element& 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;
}
} //!FBX
} //!Assimp
#endif

View File

@ -0,0 +1,170 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXImporter.h"
#include "FBXDocumentUtil.h"
namespace Assimp {
namespace FBX {
using namespace Util;
// ------------------------------------------------------------------------------------------------
NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: Object(id,element,name)
, props()
{
const Scope& 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 Element& element, const Document& doc, const std::string& name)
: NodeAttribute(id,element,doc,name)
{
const Scope& sc = GetRequiredScope(element);
const Element* const CameraId = sc["CameraId"];
const Element* const CameraName = sc["CameraName"];
const Element* const CameraIndexName = sc["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 Element& element, const Document& doc, const std::string& name)
: NodeAttribute(id,element,doc,name)
{
// empty
}
// ------------------------------------------------------------------------------------------------
Camera::~Camera()
{
// empty
}
// ------------------------------------------------------------------------------------------------
Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: NodeAttribute(id,element,doc,name)
{
// empty
}
// ------------------------------------------------------------------------------------------------
Light::~Light()
{
}
// ------------------------------------------------------------------------------------------------
Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: NodeAttribute(id,element,doc,name)
{
}
// ------------------------------------------------------------------------------------------------
Null::~Null()
{
}
// ------------------------------------------------------------------------------------------------
LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name)
: NodeAttribute(id,element,doc,name)
{
}
// ------------------------------------------------------------------------------------------------
LimbNode::~LimbNode()
{
}
}
}
#endif

1313
thirdparty/assimp/code/FBXParser.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

235
thirdparty/assimp/code/FBXParser.h vendored Normal file
View File

@ -0,0 +1,235 @@
/*
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 INCLUDED_AI_FBX_PARSER_H
#define INCLUDED_AI_FBX_PARSER_H
#include <stdint.h>
#include <map>
#include <memory>
#include <assimp/LogAux.h>
#include <assimp/fast_atof.h>
#include "FBXCompileConfig.h"
#include "FBXTokenizer.h"
namespace Assimp {
namespace FBX {
class Scope;
class Parser;
class Element;
// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
typedef std::vector< Scope* > ScopeList;
typedef std::fbx_unordered_multimap< std::string, Element* > 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(const Token& key_token, Parser& parser);
~Element();
const Scope* Compound() const {
return compound.get();
}
const Token& KeyToken() const {
return key_token;
}
const TokenList& Tokens() const {
return tokens;
}
private:
const Token& key_token;
TokenList tokens;
std::unique_ptr<Scope> compound;
};
/** 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();
const Element* operator[] (const std::string& index) const {
ElementMap::const_iterator it = elements.find(index);
return it == elements.end() ? NULL : (*it).second;
}
const Element* FindElementCaseInsensitive(const std::string& elementName) const {
const char* elementNameCStr = elementName.c_str();
for (auto element = elements.begin(); element != elements.end(); ++element)
{
if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, MAXLEN)) {
return element->second;
}
}
return NULL;
}
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 Scope& GetRootScope() const {
return *root.get();
}
bool IsBinary() const {
return is_binary;
}
private:
friend class Scope;
friend class Element;
TokenPtr AdvanceToNextToken();
TokenPtr LastToken() const;
TokenPtr CurrentToken() const;
private:
const TokenList& tokens;
TokenPtr last, current;
TokenList::const_iterator cursor;
std::unique_ptr<Scope> root;
const bool is_binary;
};
/* token parsing - this happens when building the DOM out of the parse-tree*/
uint64_t ParseTokenAsID(const Token& t, const char*& err_out);
size_t ParseTokenAsDim(const Token& t, const char*& err_out);
float ParseTokenAsFloat(const Token& t, const char*& err_out);
int ParseTokenAsInt(const Token& t, const char*& err_out);
int64_t ParseTokenAsInt64(const Token& t, const char*& err_out);
std::string ParseTokenAsString(const Token& t, const char*& err_out);
/* wrapper around ParseTokenAsXXX() with DOMError handling */
uint64_t ParseTokenAsID(const Token& t);
size_t ParseTokenAsDim(const Token& t);
float ParseTokenAsFloat(const Token& t);
int ParseTokenAsInt(const Token& t);
int64_t ParseTokenAsInt64(const Token& t);
std::string ParseTokenAsString(const Token& t);
/* read data arrays */
void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el);
void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el);
void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el);
void ParseVectorDataArray(std::vector<int>& out, const Element& el);
void ParseVectorDataArray(std::vector<float>& out, const Element& el);
void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el);
void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& e);
void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el);
bool HasElement( const Scope& sc, const std::string& index );
// extract a required element from a scope, abort if the element cannot be found
const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL);
// extract required compound scope
const Scope& GetRequiredScope(const Element& el);
// get token at a particular index
const Token& GetRequiredToken(const Element& el, unsigned int index);
// read a 4x4 matrix from an array of 16 floats
aiMatrix4x4 ReadMatrix(const Element& element);
} // ! FBX
} // ! Assimp
#endif // ! INCLUDED_AI_FBX_PARSER_H

235
thirdparty/assimp/code/FBXProperties.cpp vendored Normal file
View File

@ -0,0 +1,235 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXDocumentUtil.h"
#include "FBXProperties.h"
namespace Assimp {
namespace FBX {
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.
Property* ReadTypedProperty(const Element& 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<aiVector3D>(aiVector3D(
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 NULL;
}
// ------------------------------------------------------------------------------------------------
// 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]);
}
} //! anon
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable()
: templateProps()
, element()
{
}
// ------------------------------------------------------------------------------------------------
PropertyTable::PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps)
: templateProps(templateProps)
, element(&element)
{
const Scope& scope = GetRequiredScope(element);
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;
}
lazyProps[name] = v.second;
}
}
// ------------------------------------------------------------------------------------------------
PropertyTable::~PropertyTable()
{
for(PropertyMap::value_type& v : props) {
delete v.second;
}
}
// ------------------------------------------------------------------------------------------------
const Property* 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 NULL;
}
}
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.
std::shared_ptr<Property> prop = std::shared_ptr<Property>(ReadTypedProperty(*element.second));
// Element could not be read. Skip it.
if (!prop) continue;
// Add to result
result[element.first] = prop;
}
return result;
}
} //! FBX
} //! Assimp
#endif

185
thirdparty/assimp/code/FBXProperties.h vendored Normal file
View File

@ -0,0 +1,185 @@
/*
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 INCLUDED_AI_FBX_PROPERTIES_H
#define INCLUDED_AI_FBX_PROPERTIES_H
#include "FBXCompileConfig.h"
#include <memory>
#include <string>
namespace Assimp {
namespace FBX {
// 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;
};
typedef std::fbx_unordered_map<std::string,std::shared_ptr<Property> > DirectPropertyMap;
typedef std::fbx_unordered_map<std::string,const Property*> PropertyMap;
typedef std::fbx_unordered_map<std::string,const Element*> 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 Element& element, std::shared_ptr<const PropertyTable> templateProps);
~PropertyTable();
const Property* Get(const std::string& name) const;
// PropertyTable's need not be coupled with FBX elements so this can be NULL
const Element* GetElement() const {
return element;
}
const PropertyTable* TemplateProps() const {
return templateProps.get();
}
DirectPropertyMap GetUnparsedProperties() const;
private:
LazyPropertyMap lazyProps;
mutable PropertyMap props;
const std::shared_ptr<const PropertyTable> templateProps;
const Element* const element;
};
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
const Property* const 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 ) {
const Property* 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();
}
} //! FBX
} //! Assimp
#endif // INCLUDED_AI_FBX_PROPERTIES_H

248
thirdparty/assimp/code/FBXTokenizer.cpp vendored Normal file
View File

@ -0,0 +1,248 @@
/*
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
*/
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
// tab width for logging columns
#define ASSIMP_FBX_TAB_WIDTH 4
#include <assimp/ParsingUtils.h>
#include "FBXTokenizer.h"
#include "FBXUtil.h"
#include <assimp/Exceptional.h>
namespace Assimp {
namespace FBX {
// ------------------------------------------------------------------------------------------------
Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column)
:
#ifdef DEBUG
contents(sbegin, static_cast<size_t>(send-sbegin)),
#endif
sbegin(sbegin)
, send(send)
, type(type)
, line(line)
, column(column)
{
ai_assert(sbegin);
ai_assert(send);
// tokens must be of non-zero length
ai_assert(static_cast<size_t>(send-sbegin) > 0);
}
// ------------------------------------------------------------------------------------------------
Token::~Token()
{
}
namespace {
// ------------------------------------------------------------------------------------------------
// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX;
AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
{
throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,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 = NULL;
}
}
// ------------------------------------------------------------------------------------------------
void Tokenize(TokenList& output_tokens, const char* input)
{
ai_assert(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 = NULL, *token_end = NULL;
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;
}
}
}
} // !FBX
} // !Assimp
#endif

187
thirdparty/assimp/code/FBXTokenizer.h vendored Normal file
View File

@ -0,0 +1,187 @@
/*
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 INCLUDED_AI_FBX_TOKENIZER_H
#define INCLUDED_AI_FBX_TOKENIZER_H
#include "FBXCompileConfig.h"
#include <assimp/ai_assert.h>
#include <vector>
#include <string>
namespace Assimp {
namespace FBX {
/** 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* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column);
/** construct a binary token */
Token(const char* sbegin, const char* send, TokenType type, unsigned int 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;
}
unsigned int Offset() const {
ai_assert(IsBinary());
return offset;
}
unsigned int Line() const {
ai_assert(!IsBinary());
return line;
}
unsigned int Column() const {
ai_assert(!IsBinary());
return column;
}
private:
#ifdef DEBUG
// full string copy for the sole purpose that it nicely appears
// in msvc's debugger window.
const std::string contents;
#endif
const char* const sbegin;
const char* const send;
const TokenType type;
union {
const unsigned int line;
unsigned int offset;
};
const unsigned int column;
};
// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
typedef const 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.
* @throw DeadlyImportError 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.
* @throw DeadlyImportError if something goes wrong */
void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length);
} // ! FBX
} // ! Assimp
#endif // ! INCLUDED_AI_FBX_PARSER_H

120
thirdparty/assimp/code/FBXUtil.cpp vendored Normal file
View File

@ -0,0 +1,120 @@
/*
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 <assimp/TinyFormatter.h>
#include <string>
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
namespace Assimp {
namespace FBX {
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 "";
}
// ------------------------------------------------------------------------------------------------
std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset)
{
return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
}
// ------------------------------------------------------------------------------------------------
std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column)
{
return static_cast<std::string>( (Formatter::format() << prefix << " (line " << line << " << col " << column << ") " << text) );
}
// ------------------------------------------------------------------------------------------------
std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok)
{
if(tok->IsBinary()) {
return static_cast<std::string>( (Formatter::format() << prefix <<
" (" << TokenTypeString(tok->Type()) <<
", offset 0x" << std::hex << tok->Offset() << ") " <<
text) );
}
return static_cast<std::string>( (Formatter::format() << prefix <<
" (" << TokenTypeString(tok->Type()) <<
", line " << tok->Line() <<
", col " << tok->Column() << ") " <<
text) );
}
} // !Util
} // !FBX
} // !Assimp
#endif

105
thirdparty/assimp/code/FBXUtil.h vendored Normal file
View File

@ -0,0 +1,105 @@
/*
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.h
* @brief FBX utility functions for internal use
*/
#ifndef INCLUDED_AI_FBX_UTIL_H
#define INCLUDED_AI_FBX_UTIL_H
#include "FBXCompileConfig.h"
#include "FBXTokenizer.h"
namespace Assimp {
namespace FBX {
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;
}
};
/** 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, unsigned int 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);
}
}
}
#endif // ! INCLUDED_AI_FBX_UTIL_H

1834
thirdparty/assimp/code/FIReader.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

107
thirdparty/assimp/code/FileLogStream.h vendored Normal file
View File

@ -0,0 +1,107 @@
/*
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

@ -0,0 +1,345 @@
/*
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

@ -0,0 +1,300 @@
/*
---------------------------------------------------------------------------
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 FindDegenerates.cpp
* @brief Implementation of the FindDegenerates post-process step.
*/
// internal headers
#include "ProcessHelper.h"
#include "FindDegenerates.h"
#include <assimp/Exceptional.h>
using namespace Assimp;
//remove mesh at position 'index' from the scene
static void removeMesh(aiScene* pScene, unsigned const index);
//correct node indices to meshes and remove references to deleted mesh
static void updateSceneGraph(aiNode* pNode, unsigned const index);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess()
: mConfigRemoveDegenerates( false )
, mConfigCheckAreaOfTriangle( false ){
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FindDegeneratesProcess::~FindDegeneratesProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FindDegenerates);
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
// Get the current value of AI_CONFIG_PP_FD_REMOVE
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindDegeneratesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
//Do not process point cloud, ExecuteOnMesh works only with faces data
if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) {
removeMesh(pScene, i);
--i; //the current i is removed, do not skip the next one
}
}
ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
}
static void removeMesh(aiScene* pScene, unsigned const index) {
//we start at index and copy the pointers one position forward
//save the mesh pointer to delete it later
auto delete_me = pScene->mMeshes[index];
for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) {
pScene->mMeshes[i] = pScene->mMeshes[i+1];
}
pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr;
--(pScene->mNumMeshes);
delete delete_me;
//removing a mesh also requires updating all references to it in the scene graph
updateSceneGraph(pScene->mRootNode, index);
}
static void updateSceneGraph(aiNode* pNode, unsigned const index) {
for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
if (pNode->mMeshes[i] > index) {
--(pNode->mMeshes[i]);
continue;
}
if (pNode->mMeshes[i] == index) {
for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) {
pNode->mMeshes[j] = pNode->mMeshes[j+1];
}
--(pNode->mNumMeshes);
--i;
continue;
}
}
//recurse to all children
for (unsigned i = 0; i < pNode->mNumChildren; ++i) {
updateSceneGraph(pNode->mChildren[i], index);
}
}
static ai_real heron( ai_real a, ai_real b, ai_real c ) {
ai_real s = (a + b + c) / 2;
ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
return area;
}
static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
const ai_real lx = ( vB.x - vA.x );
const ai_real ly = ( vB.y - vA.y );
const ai_real lz = ( vB.z - vA.z );
ai_real a = lx*lx + ly*ly + lz*lz;
ai_real d = pow( a, (ai_real)0.5 );
return d;
}
static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
ai_real area = 0;
aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
ai_real a( distance3D( vA, vB ) );
ai_real b( distance3D( vB, vC ) );
ai_real c( distance3D( vC, vA ) );
area = heron( a, b, c );
return area;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported mesh
bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
mesh->mPrimitiveTypes = 0;
std::vector<bool> remove_me;
if (mConfigRemoveDegenerates) {
remove_me.resize( mesh->mNumFaces, false );
}
unsigned int deg = 0, limit;
for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
aiFace& face = mesh->mFaces[a];
bool first = true;
// check whether the face contains degenerated entries
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
// Polygons with more than 4 points are allowed to have double points, that is
// simulating polygons with holes just with concave polygons. However,
// double points may not come directly after another.
limit = face.mNumIndices;
if (face.mNumIndices > 4) {
limit = std::min( limit, i+2 );
}
for (unsigned int t = i+1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
// we have found a matching vertex position
// remove the corresponding index from the array
--face.mNumIndices;
--limit;
for (unsigned int m = t; m < face.mNumIndices; ++m) {
face.mIndices[ m ] = face.mIndices[ m+1 ];
}
--t;
// NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his
// application attempts to access this data.
face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
if(first) {
++deg;
first = false;
}
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
}
}
}
if ( mConfigCheckAreaOfTriangle ) {
if ( face.mNumIndices == 3 ) {
ai_real area = calculateAreaOfTriangle( face, mesh );
if ( area < 1e-6 ) {
if ( mConfigRemoveDegenerates ) {
remove_me[ a ] = true;
goto evil_jump_outside;
}
// todo: check for index which is corrupt.
}
}
}
}
// We need to update the primitive flags array of the mesh.
switch (face.mNumIndices)
{
case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
case 2u:
mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
break;
case 3u:
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
default:
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break;
};
evil_jump_outside:
continue;
}
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
if (mConfigRemoveDegenerates && deg) {
unsigned int n = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
{
aiFace& face_src = mesh->mFaces[a];
if (!remove_me[a]) {
aiFace& face_dest = mesh->mFaces[n++];
// Do a manual copy, keep the index array
face_dest.mNumIndices = face_src.mNumIndices;
face_dest.mIndices = face_src.mIndices;
if (&face_src != &face_dest) {
// clear source
face_src.mNumIndices = 0;
face_src.mIndices = nullptr;
}
}
else {
// Otherwise delete it if we don't need this face
delete[] face_src.mIndices;
face_src.mIndices = nullptr;
face_src.mNumIndices = 0;
}
}
// Just leave the rest of the array unreferenced, we don't care for now
mesh->mNumFaces = n;
if (!mesh->mNumFaces) {
//The whole mesh consists of degenerated faces
//signal upward, that this mesh should be deleted.
ASSIMP_LOG_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
return true;
}
}
if (deg && !DefaultLogger::isNullLogger()) {
ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives");
}
return false;
}

129
thirdparty/assimp/code/FindDegenerates.h vendored Normal file
View File

@ -0,0 +1,129 @@
/*
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_FINDDEGENERATESPROCESS_H_INC
#define AI_FINDDEGENERATESPROCESS_H_INC
#include "BaseProcess.h"
#include <assimp/mesh.h>
class FindDegeneratesProcessTest;
namespace Assimp {
// ---------------------------------------------------------------------------
/** FindDegeneratesProcess: Searches a mesh for degenerated triangles.
*/
class ASSIMP_API FindDegeneratesProcess : public BaseProcess {
public:
FindDegeneratesProcess();
~FindDegeneratesProcess();
// -------------------------------------------------------------------
// Check whether step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
// Execute step on a given mesh
///@returns true if the current mesh should be deleted, false otherwise
bool ExecuteOnMesh( aiMesh* mesh);
// -------------------------------------------------------------------
/// @brief Enable the instant removal of degenerated primitives
/// @param enabled true for enabled.
void EnableInstantRemoval(bool enabled);
// -------------------------------------------------------------------
/// @brief Check whether instant removal is currently enabled
/// @return The instant removal state.
bool IsInstantRemoval() const;
// -------------------------------------------------------------------
/// @brief Enable the area check for triangles.
/// @param enabled true for enabled.
void EnableAreaCheck( bool enabled );
// -------------------------------------------------------------------
/// @brief Check whether the area check is enabled.
/// @return The area check state.
bool isAreaCheckEnabled() const;
private:
//! Configuration option: remove degenerates faces immediately
bool mConfigRemoveDegenerates;
//! Configuration option: check for area
bool mConfigCheckAreaOfTriangle;
};
inline
void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) {
mConfigRemoveDegenerates = enabled;
}
inline
bool FindDegeneratesProcess::IsInstantRemoval() const {
return mConfigRemoveDegenerates;
}
inline
void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) {
mConfigCheckAreaOfTriangle = enabled;
}
inline
bool FindDegeneratesProcess::isAreaCheckEnabled() const {
return mConfigCheckAreaOfTriangle;
}
} // Namespace Assimp
#endif // !! AI_FINDDEGENERATESPROCESS_H_INC

View File

@ -0,0 +1,277 @@
/*
---------------------------------------------------------------------------
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 FindInstancesProcess.cpp
* @brief Implementation of the aiProcess_FindInstances postprocessing step
*/
#include "FindInstancesProcess.h"
#include <memory>
#include <stdio.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindInstancesProcess::FindInstancesProcess()
: configSpeedFlag (false)
{}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FindInstancesProcess::~FindInstancesProcess()
{}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindInstancesProcess::IsActive( unsigned int pFlags) const
{
// FindInstances makes absolutely no sense together with PreTransformVertices
// fixme: spawn error message somewhere else?
return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices);
}
// ------------------------------------------------------------------------------------------------
// Setup properties for the step
void FindInstancesProcess::SetupProperties(const Importer* pImp)
{
// AI_CONFIG_FAVOUR_SPEED
configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
}
// ------------------------------------------------------------------------------------------------
// Compare the bones of two meshes
bool CompareBones(const aiMesh* orig, const aiMesh* inst)
{
for (unsigned int i = 0; i < orig->mNumBones;++i) {
aiBone* aha = orig->mBones[i];
aiBone* oha = inst->mBones[i];
if (aha->mNumWeights != oha->mNumWeights ||
aha->mOffsetMatrix != oha->mOffsetMatrix) {
return false;
}
// compare weight per weight ---
for (unsigned int n = 0; n < aha->mNumWeights;++n) {
if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId ||
(aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) {
return false;
}
}
}
return true;
}
// ------------------------------------------------------------------------------------------------
// Update mesh indices in the node graph
void UpdateMeshIndices(aiNode* node, unsigned int* lookup)
{
for (unsigned int n = 0; n < node->mNumMeshes;++n)
node->mMeshes[n] = lookup[node->mMeshes[n]];
for (unsigned int n = 0; n < node->mNumChildren;++n)
UpdateMeshIndices(node->mChildren[n],lookup);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindInstancesProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("FindInstancesProcess begin");
if (pScene->mNumMeshes) {
// use a pseudo hash for all meshes in the scene to quickly find
// the ones which are possibly equal. This step is executed early
// in the pipeline, so we could, depending on the file format,
// have several thousand small meshes. That's too much for a brute
// everyone-against-everyone check involving up to 10 comparisons
// each.
std::unique_ptr<uint64_t[]> hashes (new uint64_t[pScene->mNumMeshes]);
std::unique_ptr<unsigned int[]> remapping (new unsigned int[pScene->mNumMeshes]);
unsigned int numMeshesOut = 0;
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
aiMesh* inst = pScene->mMeshes[i];
hashes[i] = GetMeshHash(inst);
for (int a = i-1; a >= 0; --a) {
if (hashes[i] == hashes[a])
{
aiMesh* orig = pScene->mMeshes[a];
if (!orig)
continue;
// check for hash collision .. we needn't check
// the vertex format, it *must* match due to the
// (brilliant) construction of the hash
if (orig->mNumBones != inst->mNumBones ||
orig->mNumFaces != inst->mNumFaces ||
orig->mNumVertices != inst->mNumVertices ||
orig->mMaterialIndex != inst->mMaterialIndex ||
orig->mPrimitiveTypes != inst->mPrimitiveTypes)
continue;
// up to now the meshes are equal. find an appropriate
// epsilon to compare position differences against
float epsilon = ComputePositionEpsilon(inst);
epsilon *= epsilon;
// now compare vertex positions, normals,
// tangents and bitangents using this epsilon.
if (orig->HasPositions()) {
if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
continue;
}
if (orig->HasNormals()) {
if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon))
continue;
}
if (orig->HasTangentsAndBitangents()) {
if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) ||
!CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon))
continue;
}
// use a constant epsilon for colors and UV coordinates
static const float uvEpsilon = 10e-4f;
{
unsigned int j, end = orig->GetNumUVChannels();
for(j = 0; j < end; ++j) {
if (!orig->mTextureCoords[j]) {
continue;
}
if(!CompareArrays(orig->mTextureCoords[j],inst->mTextureCoords[j],orig->mNumVertices,uvEpsilon)) {
break;
}
}
if (j != end) {
continue;
}
}
{
unsigned int j, end = orig->GetNumColorChannels();
for(j = 0; j < end; ++j) {
if (!orig->mColors[j]) {
continue;
}
if(!CompareArrays(orig->mColors[j],inst->mColors[j],orig->mNumVertices,uvEpsilon)) {
break;
}
}
if (j != end) {
continue;
}
}
// These two checks are actually quite expensive and almost *never* required.
// Almost. That's why they're still here. But there's no reason to do them
// in speed-targeted imports.
if (!configSpeedFlag) {
// It seems to be strange, but we really need to check whether the
// bones are identical too. Although it's extremely unprobable
// that they're not if control reaches here, we need to deal
// with unprobable cases, too. It could still be that there are
// equal shapes which are deformed differently.
if (!CompareBones(orig,inst))
continue;
// For completeness ... compare even the index buffers for equality
// face order & winding order doesn't care. Input data is in verbose format.
std::unique_ptr<unsigned int[]> ftbl_orig(new unsigned int[orig->mNumVertices]);
std::unique_ptr<unsigned int[]> ftbl_inst(new unsigned int[orig->mNumVertices]);
for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) {
aiFace& f = orig->mFaces[tt];
for (unsigned int nn = 0; nn < f.mNumIndices;++nn)
ftbl_orig[f.mIndices[nn]] = tt;
aiFace& f2 = inst->mFaces[tt];
for (unsigned int nn = 0; nn < f2.mNumIndices;++nn)
ftbl_inst[f2.mIndices[nn]] = tt;
}
if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int)))
continue;
}
// We're still here. Or in other words: 'inst' is an instance of 'orig'.
// Place a marker in our list that we can easily update mesh indices.
remapping[i] = remapping[a];
// Delete the instanced mesh, we don't need it anymore
delete inst;
pScene->mMeshes[i] = NULL;
break;
}
}
// If we didn't find a match for the current mesh: keep it
if (pScene->mMeshes[i]) {
remapping[i] = numMeshesOut++;
}
}
ai_assert(0 != numMeshesOut);
if (numMeshesOut != pScene->mNumMeshes) {
// Collapse the meshes array by removing all NULL entries
for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) {
if (pScene->mMeshes[i])
pScene->mMeshes[real++] = pScene->mMeshes[i];
}
// And update the node graph with our nice lookup table
UpdateMeshIndices(pScene->mRootNode,remapping.get());
// write to log
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" );
}
pScene->mNumMeshes = numMeshesOut;
} else {
ASSIMP_LOG_DEBUG("FindInstancesProcess finished. No instanced meshes found");
}
}
}

View File

@ -0,0 +1,137 @@
/*
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 FindInstancesProcess.h
* @brief Declares the aiProcess_FindInstances post-process step
*/
#ifndef AI_FINDINSTANCES_H_INC
#define AI_FINDINSTANCES_H_INC
#include "BaseProcess.h"
#include "ProcessHelper.h"
class FindInstancesProcessTest;
namespace Assimp {
// -------------------------------------------------------------------------------
/** @brief Get a pseudo(!)-hash representing a mesh.
*
* The hash is built from number of vertices, faces, primitive types,
* .... but *not* from the real mesh data. The funcction is not a perfect hash.
* @param in Input mesh
* @return Hash.
*/
inline
uint64_t GetMeshHash(aiMesh* in) {
ai_assert(nullptr != in);
// ... get an unique value representing the vertex format of the mesh
const unsigned int fhash = GetMeshVFormatUnique(in);
// and bake it with number of vertices/faces/bones/matidx/ptypes
return ((uint64_t)fhash << 32u) | ((
(in->mNumBones << 16u) ^ (in->mNumVertices) ^
(in->mNumFaces<<4u) ^ (in->mMaterialIndex<<15) ^
(in->mPrimitiveTypes<<28)) & 0xffffffff );
}
// -------------------------------------------------------------------------------
/** @brief Perform a component-wise comparison of two arrays
*
* @param first First array
* @param second Second array
* @param size Size of both arrays
* @param e Epsilon
* @return true if the arrays are identical
*/
inline
bool CompareArrays(const aiVector3D* first, const aiVector3D* second,
unsigned int size, float e) {
for (const aiVector3D* end = first+size; first != end; ++first,++second) {
if ( (*first - *second).SquareLength() >= e)
return false;
}
return true;
}
// and the same for colors ...
inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second,
unsigned int size, float e)
{
for (const aiColor4D* end = first+size; first != end; ++first,++second) {
if ( GetColorDifference(*first,*second) >= e)
return false;
}
return true;
}
// ---------------------------------------------------------------------------
/** @brief A post-processing steps to search for instanced meshes
*/
class FindInstancesProcess : public BaseProcess
{
public:
FindInstancesProcess();
~FindInstancesProcess();
public:
// -------------------------------------------------------------------
// Check whether step is active in given flags combination
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
// Execute step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Setup properties prior to executing the process
void SetupProperties(const Importer* pImp);
private:
bool configSpeedFlag;
}; // ! end class FindInstancesProcess
} // ! end namespace Assimp
#endif // !! AI_FINDINSTANCES_H_INC

View File

@ -0,0 +1,424 @@
/*
---------------------------------------------------------------------------
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 an importer's output
for data that is obviously invalid */
#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
// internal headers
#include "FindInvalidDataProcess.h"
#include "ProcessHelper.h"
#include <assimp/Macros.h>
#include <assimp/Exceptional.h>
#include <assimp/qnan.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FindInvalidDataProcess::FindInvalidDataProcess()
: configEpsilon(0.0)
, mIgnoreTexCoods( false ){
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FindInvalidDataProcess::~FindInvalidDataProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FindInvalidData);
}
// ------------------------------------------------------------------------------------------------
// Setup import configuration
void FindInvalidDataProcess::SetupProperties(const Importer* pImp) {
// Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY
configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f));
mIgnoreTexCoods = pImp->GetPropertyBool(AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS, false);
}
// ------------------------------------------------------------------------------------------------
// Update mesh references in the node graph
void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMapping) {
if (node->mNumMeshes) {
unsigned int out = 0;
for (unsigned int a = 0; a < node->mNumMeshes;++a) {
unsigned int ref = node->mMeshes[a];
if (UINT_MAX != (ref = meshMapping[ref])) {
node->mMeshes[out++] = ref;
}
}
// just let the members that are unused, that's much cheaper
// than a full array realloc'n'copy party ...
if(!(node->mNumMeshes = out)) {
delete[] node->mMeshes;
node->mMeshes = NULL;
}
}
// recursively update all children
for (unsigned int i = 0; i < node->mNumChildren;++i) {
UpdateMeshReferences(node->mChildren[i],meshMapping);
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FindInvalidDataProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindInvalidDataProcess begin");
bool out = false;
std::vector<unsigned int> meshMapping(pScene->mNumMeshes);
unsigned int real = 0;
// Process meshes
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
int result;
if ((result = ProcessMesh( pScene->mMeshes[a]))) {
out = true;
if (2 == result) {
// remove this mesh
delete pScene->mMeshes[a];
AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]);
meshMapping[a] = UINT_MAX;
continue;
}
}
pScene->mMeshes[real] = pScene->mMeshes[a];
meshMapping[a] = real++;
}
// Process animations
for (unsigned int a = 0; a < pScene->mNumAnimations;++a) {
ProcessAnimation( pScene->mAnimations[a]);
}
if (out) {
if ( real != pScene->mNumMeshes) {
if (!real) {
throw DeadlyImportError("No meshes remaining");
}
// we need to remove some meshes.
// therefore we'll also need to remove all references
// to them from the scenegraph
UpdateMeshReferences(pScene->mRootNode,meshMapping);
pScene->mNumMeshes = real;
}
ASSIMP_LOG_INFO("FindInvalidDataProcess finished. Found issues ...");
} else {
ASSIMP_LOG_DEBUG("FindInvalidDataProcess finished. Everything seems to be OK.");
}
}
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/,
const std::vector<bool>& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/)
{
return nullptr;
}
// ------------------------------------------------------------------------------------------------
template <>
inline
const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned int size,
const std::vector<bool>& dirtyMask, bool mayBeIdentical , bool mayBeZero ) {
bool b = false;
unsigned int cnt = 0;
for (unsigned int i = 0; i < size;++i) {
if (dirtyMask.size() && dirtyMask[i]) {
continue;
}
++cnt;
const aiVector3D& v = arr[i];
if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) {
return "INF/NAN was found in a vector component";
}
if (!mayBeZero && !v.x && !v.y && !v.z ) {
return "Found zero-length vector";
}
if (i && v != arr[i-1])b = true;
}
if (cnt > 1 && !b && !mayBeIdentical) {
return "All vectors are identical";
}
return nullptr;
}
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
bool ProcessArray(T*& in, unsigned int num,const char* name,
const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) {
const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero);
if (err) {
ASSIMP_LOG_ERROR_F( "FindInvalidDataProcess fails on mesh ", name, ": ", err);
delete[] in;
in = NULL;
return true;
}
return false;
}
// ------------------------------------------------------------------------------------------------
template <typename T>
AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, ai_real epsilon);
// ------------------------------------------------------------------------------------------------
AI_FORCE_INLINE bool EpsilonCompare(ai_real n, ai_real s, ai_real epsilon) {
return std::fabs(n-s)>epsilon;
}
// ------------------------------------------------------------------------------------------------
template <>
bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, ai_real epsilon) {
return
EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
EpsilonCompare(n.mValue.z,s.mValue.z,epsilon);
}
// ------------------------------------------------------------------------------------------------
template <>
bool EpsilonCompare<aiQuatKey>(const aiQuatKey& n, const aiQuatKey& s, ai_real epsilon) {
return
EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) &&
EpsilonCompare(n.mValue.w,s.mValue.w,epsilon);
}
// ------------------------------------------------------------------------------------------------
template <typename T>
inline
bool AllIdentical(T* in, unsigned int num, ai_real epsilon) {
if (num <= 1) {
return true;
}
if (fabs(epsilon) > 0.f) {
for (unsigned int i = 0; i < num-1;++i) {
if (!EpsilonCompare(in[i],in[i+1],epsilon)) {
return false;
}
}
} else {
for (unsigned int i = 0; i < num-1;++i) {
if (in[i] != in[i+1]) {
return false;
}
}
}
return true;
}
// ------------------------------------------------------------------------------------------------
// Search an animation for invalid content
void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) {
// Process all animation channels
for ( unsigned int a = 0; a < anim->mNumChannels; ++a ) {
ProcessAnimationChannel( anim->mChannels[a]);
}
}
// ------------------------------------------------------------------------------------------------
void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
ai_assert( nullptr != anim );
if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) {
ai_assert_entry();
return;
}
// Check whether all values in a tracks are identical - in this case
// we can remove al keys except one.
// POSITIONS
int i = 0;
if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon)) {
aiVectorKey v = anim->mPositionKeys[0];
// Reallocate ... we need just ONE element, it makes no sense to reuse the array
delete[] anim->mPositionKeys;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1];
anim->mPositionKeys[0] = v;
i = 1;
}
// ROTATIONS
if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon)) {
aiQuatKey v = anim->mRotationKeys[0];
// Reallocate ... we need just ONE element, it makes no sense to reuse the array
delete[] anim->mRotationKeys;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1];
anim->mRotationKeys[0] = v;
i = 1;
}
// SCALINGS
if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon)) {
aiVectorKey v = anim->mScalingKeys[0];
// Reallocate ... we need just ONE element, it makes no sense to reuse the array
delete[] anim->mScalingKeys;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1];
anim->mScalingKeys[0] = v;
i = 1;
}
if ( 1 == i ) {
ASSIMP_LOG_WARN("Simplified dummy tracks with just one key");
}
}
// ------------------------------------------------------------------------------------------------
// Search a mesh for invalid contents
int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
{
bool ret = false;
std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0);
// Ignore elements that are not referenced by vertices.
// (they are, for example, caused by the FindDegenerates step)
for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) {
const aiFace& f = pMesh->mFaces[m];
for (unsigned int i = 0; i < f.mNumIndices; ++i) {
dirtyMask[f.mIndices[i]] = false;
}
}
// Process vertex positions
if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) {
ASSIMP_LOG_ERROR("Deleting mesh: Unable to continue without vertex positions");
return 2;
}
// process texture coordinates
if (!mIgnoreTexCoods) {
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) {
if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) {
pMesh->mNumUVComponents[i] = 0;
// delete all subsequent texture coordinate sets.
for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
delete[] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = NULL;
pMesh->mNumUVComponents[a] = 0;
}
ret = true;
}
}
}
// -- we don't validate vertex colors, it's difficult to say whether
// they are invalid or not.
// Normals and tangents are undefined for point and line faces.
if (pMesh->mNormals || pMesh->mTangents) {
if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes ||
aiPrimitiveType_LINE & pMesh->mPrimitiveTypes)
{
if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes ||
aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes)
{
// We need to update the lookup-table
for (unsigned int m = 0; m < pMesh->mNumFaces;++m) {
const aiFace& f = pMesh->mFaces[ m ];
if (f.mNumIndices < 3) {
dirtyMask[f.mIndices[0]] = true;
if (f.mNumIndices == 2) {
dirtyMask[f.mIndices[1]] = true;
}
}
}
}
// Normals, tangents and bitangents are undefined for
// the whole mesh (and should not even be there)
else {
return ret;
}
}
// Process mesh normals
if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices,
"normals",dirtyMask,true,false))
ret = true;
// Process mesh tangents
if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) {
delete[] pMesh->mBitangents; pMesh->mBitangents = NULL;
ret = true;
}
// Process mesh bitangents
if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) {
delete[] pMesh->mTangents; pMesh->mTangents = NULL;
ret = true;
}
}
return ret ? 1 : 0;
}
#endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS

View File

@ -0,0 +1,105 @@
/*
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 an importer's output
* for data that is obviously invalid
*/
#ifndef AI_FINDINVALIDDATA_H_INC
#define AI_FINDINVALIDDATA_H_INC
#include "BaseProcess.h"
#include <assimp/types.h>
#include <assimp/anim.h>
struct aiMesh;
class FindInvalidDataProcessTest;
namespace Assimp {
// ---------------------------------------------------------------------------
/** The FindInvalidData post-processing step. It searches the mesh data
* for parts that are obviously invalid and removes them.
*
* Originally this was a workaround for some models written by Blender
* which have zero normal vectors. */
class ASSIMP_API FindInvalidDataProcess : public BaseProcess {
public:
FindInvalidDataProcess();
~FindInvalidDataProcess();
// -------------------------------------------------------------------
//
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
// Setup import settings
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
// Run the step
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given mesh
* @param pMesh The mesh to process.
* @return 0 - nothing, 1 - removed sth, 2 - please delete me */
int ProcessMesh( aiMesh* pMesh);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given animation
* @param anim The animation to process. */
void ProcessAnimation (aiAnimation* anim);
// -------------------------------------------------------------------
/** Executes the post-processing step on the given anim channel
* @param anim The animation channel to process.*/
void ProcessAnimationChannel (aiNodeAnim* anim);
private:
ai_real configEpsilon;
bool mIgnoreTexCoods;
};
} // end of namespace Assimp
#endif // AI_AI_FINDINVALIDDATA_H_INC

View File

@ -0,0 +1,184 @@
/*
---------------------------------------------------------------------------
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 post processing step to invert
* all normals in meshes with infacing normals.
*/
// internal headers
#include "FixNormalsStep.h"
#include <assimp/StringUtils.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <stdio.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
FixInfacingNormalsProcess::FixInfacingNormalsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FixInfacingNormalsProcess::~FixInfacingNormalsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_FixInfacingNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void FixInfacingNormalsProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess begin");
bool bHas( false );
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
if (ProcessMesh(pScene->mMeshes[a], a)) {
bHas = true;
}
}
if (bHas) {
ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. Found issues.");
} else {
ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. No changes to the scene.");
}
}
// ------------------------------------------------------------------------------------------------
// Apply the step to the mesh
bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index)
{
ai_assert(nullptr != pcMesh);
// Nothing to do if there are no model normals
if (!pcMesh->HasNormals()) {
return false;
}
// Compute the bounding box of both the model vertices + normals and
// the unmodified model vertices. Then check whether the first BB
// is smaller than the second. In this case we can assume that the
// normals need to be flipped, although there are a few special cases ..
// convex, concave, planar models ...
aiVector3D vMin0 (1e10f,1e10f,1e10f);
aiVector3D vMin1 (1e10f,1e10f,1e10f);
aiVector3D vMax0 (-1e10f,-1e10f,-1e10f);
aiVector3D vMax1 (-1e10f,-1e10f,-1e10f);
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x);
vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y);
vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z);
vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x);
vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y);
vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z);
const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i];
vMin0.x = std::min(vMin0.x,vWithNormal.x);
vMin0.y = std::min(vMin0.y,vWithNormal.y);
vMin0.z = std::min(vMin0.z,vWithNormal.z);
vMax0.x = std::max(vMax0.x,vWithNormal.x);
vMax0.y = std::max(vMax0.y,vWithNormal.y);
vMax0.z = std::max(vMax0.z,vWithNormal.z);
}
const float fDelta0_x = (vMax0.x - vMin0.x);
const float fDelta0_y = (vMax0.y - vMin0.y);
const float fDelta0_z = (vMax0.z - vMin0.z);
const float fDelta1_x = (vMax1.x - vMin1.x);
const float fDelta1_y = (vMax1.y - vMin1.y);
const float fDelta1_z = (vMax1.z - vMin1.z);
// Check whether the boxes are overlapping
if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false;
if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false;
if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false;
// Check whether this is a planar surface
const float fDelta1_yz = fDelta1_y * fDelta1_z;
if (fDelta1_x < 0.05f * std::sqrt( fDelta1_yz ))return false;
if (fDelta1_y < 0.05f * std::sqrt( fDelta1_z * fDelta1_x ))return false;
if (fDelta1_z < 0.05f * std::sqrt( fDelta1_y * fDelta1_x ))return false;
// now compare the volumes of the bounding boxes
if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) {
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index);
}
// Invert normals
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
pcMesh->mNormals[i] *= -1.0f;
// ... and flip faces
for (unsigned int i = 0; i < pcMesh->mNumFaces;++i)
{
aiFace& face = pcMesh->mFaces[i];
for( unsigned int b = 0; b < face.mNumIndices / 2; b++)
std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]);
}
return true;
}
return false;
}

91
thirdparty/assimp/code/FixNormalsStep.h vendored Normal file
View File

@ -0,0 +1,91 @@
/*
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 fix infacing normals */
#ifndef AI_FIXNORMALSPROCESS_H_INC
#define AI_FIXNORMALSPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The FixInfacingNormalsProcess tries to determine whether the normal
* vectors of an object are facing inwards. In this case they will be
* flipped.
*/
class FixInfacingNormalsProcess : public BaseProcess {
public:
FixInfacingNormalsProcess();
~FixInfacingNormalsProcess();
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @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;
// -------------------------------------------------------------------
/** 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);
protected:
// -------------------------------------------------------------------
/** Executes the step on the given mesh
* @param pMesh The mesh to process.
*/
bool ProcessMesh( aiMesh* pMesh, unsigned int index);
};
} // end of namespace Assimp
#endif // AI_FIXNORMALSPROCESS_H_INC

View File

@ -0,0 +1,146 @@
/*
---------------------------------------------------------------------------
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 post processing step to generate face
* normals for all imported faces.
*/
#include "GenFaceNormalsProcess.h"
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exceptional.h>
#include <assimp/qnan.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenFaceNormalsProcess::GenFaceNormalsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenFaceNormalsProcess::~GenFaceNormalsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const {
force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
return (pFlags & aiProcess_GenNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenFaceNormalsProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
}
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if(this->GenMeshFaceNormals( pScene->mMeshes[a])) {
bHas = true;
}
}
if (bHas) {
ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. "
"Face normals have been calculated");
} else {
ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. "
"Normals are already there");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
{
if (NULL != pMesh->mNormals) {
if (force_) delete[] pMesh->mNormals;
else return false;
}
// If the mesh consists of lines and/or points but not of
// triangles or higher-order polygons the normal vectors
// are undefined.
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes");
return false;
}
// allocate an array to hold the output normals
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
const float qnan = get_qnan();
// iterate through all faces and compute per-face normals but store them per-vertex.
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
const aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices < 3) {
// either a point or a line -> no well-defined normal vector
for (unsigned int i = 0;i < face.mNumIndices;++i) {
pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
}
continue;
}
const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
for (unsigned int i = 0;i < face.mNumIndices;++i) {
pMesh->mNormals[face.mIndices[i]] = vNor;
}
}
return true;
}

View File

@ -0,0 +1,87 @@
/*
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 compute face normals for all loaded faces*/
#ifndef AI_GENFACENORMALPROCESS_H_INC
#define AI_GENFACENORMALPROCESS_H_INC
#include "BaseProcess.h"
#include <assimp/mesh.h>
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
*/
class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess
{
public:
GenFaceNormalsProcess();
~GenFaceNormalsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @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;
// -------------------------------------------------------------------
/** 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);
private:
bool GenMeshFaceNormals(aiMesh* pcMesh);
mutable bool force_ = false;
};
} // end of namespace Assimp
#endif // !!AI_GENFACENORMALPROCESS_H_INC

View File

@ -0,0 +1,239 @@
/*
---------------------------------------------------------------------------
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 post processing step to generate face
* normals for all imported faces.
*/
// internal headers
#include "GenVertexNormalsProcess.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h>
#include <assimp/qnan.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenVertexNormalsProcess::GenVertexNormalsProcess()
: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenVertexNormalsProcess::~GenVertexNormalsProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
{
force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
return (pFlags & aiProcess_GenSmoothNormals) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
{
// Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property
configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0);
configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("GenVertexNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
}
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
if(GenMeshVertexNormals( pScene->mMeshes[a],a))
bHas = true;
}
if (bHas) {
ASSIMP_LOG_INFO("GenVertexNormalsProcess finished. "
"Vertex normals have been calculated");
} else {
ASSIMP_LOG_DEBUG("GenVertexNormalsProcess finished. "
"Normals are already there");
}
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex)
{
if (NULL != pMesh->mNormals) {
if (force_) delete[] pMesh->mNormals;
else return false;
}
// If the mesh consists of lines and/or points but not of
// triangles or higher-order polygons the normal vectors
// are undefined.
if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
{
ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes");
return false;
}
// Allocate the array to hold the output normals
const float qnan = std::numeric_limits<ai_real>::quiet_NaN();
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
// Compute per-face normals but store them per-vertex
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
const aiFace& face = pMesh->mFaces[a];
if (face.mNumIndices < 3)
{
// either a point or a line -> no normal vector
for (unsigned int i = 0;i < face.mNumIndices;++i) {
pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
}
continue;
}
const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
for (unsigned int i = 0;i < face.mNumIndices;++i) {
pMesh->mNormals[face.mIndices[i]] = vNor;
}
}
// Set up a SpatialSort to quickly find all vertices close to a given position
// check whether we can reuse the SpatialSort of a previous step.
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
ai_real posEpsilon = ai_real( 1e-5 );
if (shared) {
std::vector<std::pair<SpatialSort,ai_real> >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf)
{
std::pair<SpatialSort,ai_real>& blubb = avf->operator [] (meshIndex);
vertexFinder = &blubb.first;
posEpsilon = blubb.second;
}
}
if (!vertexFinder) {
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
vertexFinder = &_vertexFinder;
posEpsilon = ComputePositionEpsilon(pMesh);
}
std::vector<unsigned int> verticesFound;
aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) {
// There is no angle limit. Thus all vertices with positions close
// to each other will receive the same vertex normal. This allows us
// to optimize the whole algorithm a little bit ...
std::vector<bool> abHad(pMesh->mNumVertices,false);
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
if (abHad[i]) {
continue;
}
// Get all vertices that share this one ...
vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound);
aiVector3D pcNor;
for (unsigned int a = 0; a < verticesFound.size(); ++a) {
const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
if (is_not_qnan(v.x))pcNor += v;
}
pcNor.NormalizeSafe();
// Write the smoothed normal back to all affected normals
for (unsigned int a = 0; a < verticesFound.size(); ++a)
{
unsigned int vidx = verticesFound[a];
pcNew[vidx] = pcNor;
abHad[vidx] = true;
}
}
}
// Slower code path if a smooth angle is set. There are many ways to achieve
// the effect, this one is the most straightforward one.
else {
const ai_real fLimit = std::cos(configMaxAngle);
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
// Get all vertices that share this one ...
vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound);
aiVector3D vr = pMesh->mNormals[i];
aiVector3D pcNor;
for (unsigned int a = 0; a < verticesFound.size(); ++a) {
aiVector3D v = pMesh->mNormals[verticesFound[a]];
// Check whether the angle between the two normals is not too large.
// Skip the angle check on our own normal to avoid false negatives
// (v*v is not guaranteed to be 1.0 for all unit vectors v)
if (is_not_qnan(v.x) && (verticesFound[a] == i || (v * vr >= fLimit)))
pcNor += v;
}
pcNew[i] = pcNor.NormalizeSafe();
}
}
delete[] pMesh->mNormals;
pMesh->mNormals = pcNew;
return true;
}

View File

@ -0,0 +1,115 @@
/*
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 compute vertex normals
for all loaded vertizes */
#ifndef AI_GENVERTEXNORMALPROCESS_H_INC
#define AI_GENVERTEXNORMALPROCESS_H_INC
#include "BaseProcess.h"
#include <assimp/mesh.h>
class GenNormalsTest;
namespace Assimp {
// ---------------------------------------------------------------------------
/** The GenFaceNormalsProcess computes vertex normals for all vertizes
*/
class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
{
public:
GenVertexNormalsProcess();
~GenVertexNormalsProcess();
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.
*/
void SetupProperties(const Importer* pImp);
// -------------------------------------------------------------------
/** 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);
// setter for configMaxAngle
inline void SetMaxSmoothAngle(ai_real f)
{
configMaxAngle =f;
}
public:
// -------------------------------------------------------------------
/** Computes normals for a specific mesh
* @param pcMesh Mesh
* @param meshIndex Index of the mesh
* @return true if vertex normals have been computed
*/
bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex);
private:
/** Configuration option: maximum smoothing angle, in radians*/
ai_real configMaxAngle;
mutable bool force_ = false;
};
} // end of namespace Assimp
#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC

1171
thirdparty/assimp/code/Importer.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

247
thirdparty/assimp/code/Importer.h vendored Normal file
View File

@ -0,0 +1,247 @@
/*
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

@ -0,0 +1,371 @@
/*
---------------------------------------------------------------------------
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 "XFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
# include "AMFImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
# include "3DSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
# include "MD3Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
# include "MDLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
# include "MD2Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
# include "PlyLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
# include "ASELoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
# include "ObjFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
# include "HMPLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
# include "SMDLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
# include "MDCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
# include "MD5Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
# include "STLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
# include "LWOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
# include "DXFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
# include "NFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
# include "RawLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
# include "SIBImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
# include "OFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
# include "ACLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
# include "BVHLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
# include "IRRMeshLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
# include "IRRLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
# include "Q3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
# include "B3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
# include "ColladaLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
# include "TerragenLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
# include "CSMLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
# include "UnrealLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
# include "LWSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
# include "OgreImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
# include "OpenGEXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
# include "MS3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
# include "COBLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
# include "BlenderLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
# include "Q3BSPFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
# include "NDOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
# include "Importer/IFC/IFCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
# include "XGLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
# include "FBXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
# include "AssbinLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
# include "glTFImporter.h"
# include "glTF2Importer.h"
#endif
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
# include "C4DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
# include "D3MFImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
# include "X3DImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
# include "MMDImporter.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_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]=NULL;
}//for
}
} // namespace Assimp

View File

@ -0,0 +1,386 @@
/*
---------------------------------------------------------------------------
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 post processing step to improve the cache locality of a mesh.
* <br>
* The algorithm is roughly basing on this paper:
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
* .. although overdraw rduction isn't implemented yet ...
*/
// internal headers
#include "ImproveCacheLocality.h"
#include "VertexTriangleAdjacency.h"
#include <assimp/StringUtils.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <stdio.h>
#include <stack>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() {
configCacheDepth = PP_ICL_PTCACHE_SIZE;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_ImproveCacheLocality) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration
void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp)
{
// AI_CONFIG_PP_ICL_PTCACHE_SIZE controls the target cache size for the optimizer
configCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
{
if (!pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes");
return;
}
ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess begin");
float out = 0.f;
unsigned int numf = 0, numm = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++){
const float res = ProcessMesh( pScene->mMeshes[a],a);
if (res) {
numf += pScene->mMeshes[a]->mNumFaces;
out += res;
++numm;
}
}
if (!DefaultLogger::isNullLogger()) {
if (numf > 0) {
ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf);
}
ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. ");
}
}
// ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh
float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
{
// TODO: rewrite this to use std::vector or boost::shared_array
ai_assert(NULL != pMesh);
// Check whether the input data is valid
// - there must be vertices and faces
// - all faces must be triangulated or we can't operate on them
if (!pMesh->HasFaces() || !pMesh->HasPositions())
return 0.f;
if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) {
ASSIMP_LOG_ERROR("This algorithm works on triangle meshes only");
return 0.f;
}
if(pMesh->mNumVertices <= configCacheDepth) {
return 0.f;
}
float fACMR = 3.f;
const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
// Input ACMR is for logging purposes only
if (!DefaultLogger::isNullLogger()) {
unsigned int* piFIFOStack = new unsigned int[configCacheDepth];
memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
unsigned int* piCur = piFIFOStack;
const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
// count the number of cache misses
unsigned int iCacheMisses = 0;
for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) {
for (unsigned int qq = 0; qq < 3;++qq) {
bool bInCache = false;
for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp) {
if (*pp == pcFace->mIndices[qq]) {
// the vertex is in cache
bInCache = true;
break;
}
}
if (!bInCache) {
++iCacheMisses;
if (piCurEnd == piCur) {
piCur = piFIFOStack;
}
*piCur++ = pcFace->mIndices[qq];
}
}
}
delete[] piFIFOStack;
fACMR = (float)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR) {
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
// mesh, otherwise this value would normally be at least minimally
// smaller than 3.0 ...
ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum);
ASSIMP_LOG_WARN(szBuff);
return 0.f;
}
}
// first we need to build a vertex-triangle adjacency list
VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true);
// build a list to store per-vertex caching time stamps
unsigned int* const piCachingStamps = new unsigned int[pMesh->mNumVertices];
memset(piCachingStamps,0x0,pMesh->mNumVertices*sizeof(unsigned int));
// allocate an empty output index buffer. We store the output indices in one large array.
// Since the number of triangles won't change the input faces can be reused. This is how
// we save thousands of redundant mini allocations for aiFace::mIndices
const unsigned int iIdxCnt = pMesh->mNumFaces*3;
unsigned int* const piIBOutput = new unsigned int[iIdxCnt];
unsigned int* piCSIter = piIBOutput;
// allocate the flag array to hold the information
// whether a face has already been emitted or not
std::vector<bool> abEmitted(pMesh->mNumFaces,false);
// dead-end vertex index stack
std::stack<unsigned int, std::vector<unsigned int> > sDeadEndVStack;
// create a copy of the piNumTriPtr buffer
unsigned int* const piNumTriPtr = adj.mLiveTriangles;
const std::vector<unsigned int> piNumTriPtrNoModify(piNumTriPtr, piNumTriPtr + pMesh->mNumVertices);
// get the largest number of referenced triangles and allocate the "candidate buffer"
unsigned int iMaxRefTris = 0; {
const unsigned int* piCur = adj.mLiveTriangles;
const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices;
for (;piCur != piCurEnd;++piCur) {
iMaxRefTris = std::max(iMaxRefTris,*piCur);
}
}
ai_assert(iMaxRefTris > 0);
unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
unsigned int iCacheMisses = 0;
// ...................................................................................
/** PSEUDOCODE for the algorithm
A = Build-Adjacency(I) Vertex-triangle adjacency
L = Get-Triangle-Counts(A) Per-vertex live triangle counts
C = Zero(Vertex-Count(I)) Per-vertex caching time stamps
D = Empty-Stack() Dead-end vertex stack
E = False(Triangle-Count(I)) Per triangle emitted flag
O = Empty-Index-Buffer() Empty output buffer
f = 0 Arbitrary starting vertex
s = k+1, i = 1 Time stamp and cursor
while f >= 0 For all valid fanning vertices
N = Empty-Set() 1-ring of next candidates
for each Triangle t in Neighbors(A, f)
if !Emitted(E,t)
for each Vertex v in t
Append(O,v) Output vertex
Push(D,v) Add to dead-end stack
Insert(N,v) Register as candidate
L[v] = L[v]-1 Decrease live triangle count
if s-C[v] > k If not in cache
C[v] = s Set time stamp
s = s+1 Increment time stamp
E[t] = true Flag triangle as emitted
Select next fanning vertex
f = Get-Next-Vertex(I,i,k,N,C,s,L,D)
return O
*/
// ...................................................................................
int ivdx = 0;
int ics = 1;
int iStampCnt = configCacheDepth+1;
while (ivdx >= 0) {
unsigned int icnt = piNumTriPtrNoModify[ivdx];
unsigned int* piList = adj.GetAdjacentTriangles(ivdx);
unsigned int* piCurCandidate = piCandidates;
// get all triangles in the neighborhood
for (unsigned int tri = 0; tri < icnt;++tri) {
// if they have not yet been emitted, add them to the output IB
const unsigned int fidx = *piList++;
if (!abEmitted[fidx]) {
// so iterate through all vertices of the current triangle
const aiFace* pcFace = &pMesh->mFaces[ fidx ];
unsigned nind = pcFace->mNumIndices;
for (unsigned ind = 0; ind < nind; ind++) {
unsigned dp = pcFace->mIndices[ind];
// the current vertex won't have any free triangles after this step
if (ivdx != (int)dp) {
// append the vertex to the dead-end stack
sDeadEndVStack.push(dp);
// register as candidate for the next step
*piCurCandidate++ = dp;
// decrease the per-vertex triangle counts
piNumTriPtr[dp]--;
}
// append the vertex to the output index buffer
*piCSIter++ = dp;
// if the vertex is not yet in cache, set its cache count
if (iStampCnt-piCachingStamps[dp] > configCacheDepth) {
piCachingStamps[dp] = iStampCnt++;
++iCacheMisses;
}
}
// flag triangle as emitted
abEmitted[fidx] = true;
}
}
// the vertex has now no living adjacent triangles anymore
piNumTriPtr[ivdx] = 0;
// get next fanning vertex
ivdx = -1;
int max_priority = -1;
for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) {
const unsigned int dp = *piCur;
// must have live triangles
if (piNumTriPtr[dp] > 0) {
int priority = 0;
// will the vertex be in cache, even after fanning occurs?
unsigned int tmp;
if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth) {
priority = tmp;
}
// keep best candidate
if (priority > max_priority) {
max_priority = priority;
ivdx = dp;
}
}
}
// did we reach a dead end?
if (-1 == ivdx) {
// need to get a non-local vertex for which we have a good chance that it is still
// in the cache ...
while (!sDeadEndVStack.empty()) {
unsigned int iCachedIdx = sDeadEndVStack.top();
sDeadEndVStack.pop();
if (piNumTriPtr[ iCachedIdx ] > 0) {
ivdx = iCachedIdx;
break;
}
}
if (-1 == ivdx) {
// well, there isn't such a vertex. Simply get the next vertex in input order and
// hope it is not too bad ...
while (ics < (int)pMesh->mNumVertices) {
++ics;
if (piNumTriPtr[ics] > 0) {
ivdx = ics;
break;
}
}
}
}
}
float fACMR2 = 0.0f;
if (!DefaultLogger::isNullLogger()) {
fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
// very intense verbose logging ... prepare for much text if there are many meshes
if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
ASSIMP_LOG_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f);
}
fACMR2 *= pMesh->mNumFaces;
}
// sort the output index buffer back to the input array
piCSIter = piIBOutput;
for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) {
unsigned nind = pcFace->mNumIndices;
unsigned * ind = pcFace->mIndices;
if (nind > 0) ind[0] = *piCSIter++;
if (nind > 1) ind[1] = *piCSIter++;
if (nind > 2) ind[2] = *piCSIter++;
}
// delete temporary storage
delete[] piCachingStamps;
delete[] piIBOutput;
delete[] piCandidates;
return fACMR2;
}

View File

@ -0,0 +1,100 @@
/*
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 reorder faces for
better cache locality*/
#ifndef AI_IMPROVECACHELOCALITY_H_INC
#define AI_IMPROVECACHELOCALITY_H_INC
#include "BaseProcess.h"
#include <assimp/types.h>
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The ImproveCacheLocalityProcess reorders all faces for improved vertex
* cache locality. It tries to arrange all faces to fans and to render
* faces which share vertices directly one after the other.
*
* @note This step expects triagulated input data.
*/
class ImproveCacheLocalityProcess : public BaseProcess
{
public:
ImproveCacheLocalityProcess();
~ImproveCacheLocalityProcess();
public:
// -------------------------------------------------------------------
// Check whether the pp step is active
bool IsActive( unsigned int pFlags) const;
// -------------------------------------------------------------------
// Executes the pp step on a given scene
void Execute( aiScene* pScene);
// -------------------------------------------------------------------
// Configures the pp step
void SetupProperties(const Importer* pImp);
protected:
// -------------------------------------------------------------------
/** Executes the postprocessing step on the given mesh
* @param pMesh The mesh to process.
* @param meshNum Index of the mesh to process
*/
float ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
private:
//! Configuration parameter: specifies the size of the cache to
//! optimize the vertex data for.
unsigned int configCacheDepth;
};
} // end of namespace Assimp
#endif // AI_IMPROVECACHELOCALITY_H_INC

View File

@ -0,0 +1,463 @@
/*
---------------------------------------------------------------------------
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 post processing step to join identical vertices
* for all imported meshes
*/
#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
#include "JoinVerticesProcess.h"
#include "ProcessHelper.h"
#include <assimp/Vertex.h>
#include <assimp/TinyFormatter.h>
#include <stdio.h>
#include <unordered_set>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
JoinVerticesProcess::JoinVerticesProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
JoinVerticesProcess::~JoinVerticesProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void JoinVerticesProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("JoinVerticesProcess begin");
// get the total number of vertices BEFORE the step is executed
int iNumOldVertices = 0;
if (!DefaultLogger::isNullLogger()) {
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
}
}
// execute the step
int iNumVertices = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
// if logging is active, print detailed statistics
if (!DefaultLogger::isNullLogger()) {
if (iNumOldVertices == iNumVertices) {
ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
} else {
ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
" out: ", iNumVertices, " | ~",
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
}
}
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
}
namespace {
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex)
{
// A little helper to find locally close vertices faster.
// Try to reuse the lookup table from the last step.
const static float epsilon = 1e-5f;
// Squared because we check against squared length of the vector difference
static const float squareEpsilon = epsilon * epsilon;
// Square compare is useful for animeshes vertices compare
if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) {
return false;
}
// We just test the other attributes even if they're not present in the mesh.
// In this case they're initialized to 0 so the comparison succeeds.
// By this method the non-present attributes are effectively ignored in the comparison.
if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) {
return false;
}
if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) {
return false;
}
// Usually we won't have vertex colors or multiple UVs, so we can skip from here
// Actually this increases runtime performance slightly, at least if branch
// prediction is on our side.
if (complex) {
for (int i = 0; i < 8; i++) {
if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) {
return false;
}
if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) {
return false;
}
}
}
return true;
}
template<class XMesh>
void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
// replace vertex data with the unique data sets
pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
// ----------------------------------------------------------------------------
// NOTE - we're *not* calling Vertex::SortBack() because it would check for
// presence of every single vertex component once PER VERTEX. And our CPU
// dislikes branches, even if they're easily predictable.
// ----------------------------------------------------------------------------
// Position, if present (check made for aiAnimMesh)
if (pMesh->mVertices)
{
delete [] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mVertices[a] = uniqueVertices[a].position;
}
}
// Normals, if present
if (pMesh->mNormals)
{
delete [] pMesh->mNormals;
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mNormals[a] = uniqueVertices[a].normal;
}
}
// Tangents, if present
if (pMesh->mTangents)
{
delete [] pMesh->mTangents;
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mTangents[a] = uniqueVertices[a].tangent;
}
}
// Bitangents as well
if (pMesh->mBitangents)
{
delete [] pMesh->mBitangents;
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
}
}
// Vertex colors
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++)
{
delete [] pMesh->mColors[a];
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
}
}
// Texture coords
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
{
delete [] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
}
}
}
} // namespace
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
{
static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8");
static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8");
// Return early if we don't have any positions
if (!pMesh->HasPositions() || !pMesh->HasFaces()) {
return 0;
}
// We should care only about used vertices, not all of them
// (this can happen due to original file vertices buffer being used by
// multiple meshes)
std::unordered_set<unsigned int> usedVertexIndices;
usedVertexIndices.reserve(pMesh->mNumVertices);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++) {
usedVertexIndices.insert(face.mIndices[b]);
}
}
// We'll never have more vertices afterwards.
std::vector<Vertex> uniqueVertices;
uniqueVertices.reserve( pMesh->mNumVertices);
// For each vertex the index of the vertex it was replaced by.
// Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark
// whether a new vertex was created for the index (true) or if it was replaced by an existing
// unique vertex (false). This saves an additional std::vector<bool> and greatly enhances
// branching performance.
static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff");
std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
// float posEpsilonSqr;
SpatialSort* vertexFinder = NULL;
SpatialSort _vertexFinder;
typedef std::pair<SpatialSort,float> SpatPair;
if (shared) {
std::vector<SpatPair >* avf;
shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
if (avf) {
SpatPair& blubb = (*avf)[meshIndex];
vertexFinder = &blubb.first;
// posEpsilonSqr = blubb.second;
}
}
if (!vertexFinder) {
// bad, need to compute it.
_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
vertexFinder = &_vertexFinder;
// posEpsilonSqr = ComputePositionEpsilon(pMesh);
}
// Again, better waste some bytes than a realloc ...
std::vector<unsigned int> verticesFound;
verticesFound.reserve(10);
// Run an optimized code path if we don't have multiple UVs or vertex colors.
// This should yield false in more than 99% of all imports ...
const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
// We'll never have more vertices afterwards.
std::vector<std::vector<Vertex>> uniqueAnimatedVertices;
if (hasAnimMeshes) {
uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes);
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
}
}
// Now check each vertex if it brings something new to the table
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
continue;
}
// collect the vertex data
Vertex v(pMesh,a);
// collect all vertices that are close enough to the given position
vertexFinder->FindIdenticalPositions( v.position, verticesFound);
unsigned int matchIndex = 0xffffffff;
// check all unique vertices close to the position if this vertex is already present among them
for( unsigned int b = 0; b < verticesFound.size(); b++) {
const unsigned int vidx = verticesFound[b];
const unsigned int uidx = replaceIndex[ vidx];
if( uidx & 0x80000000)
continue;
const Vertex& uv = uniqueVertices[ uidx];
if (!areVerticesEqual(v, uv, complex)) {
continue;
}
if (hasAnimMeshes) {
// If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology)
// NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces)
bool breaksAnimMesh = false;
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx];
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) {
breaksAnimMesh = true;
break;
}
}
if (breaksAnimMesh) {
continue;
}
}
// we're still here -> this vertex perfectly matches our given vertex
matchIndex = uidx;
break;
}
// found a replacement vertex among the uniques?
if( matchIndex != 0xffffffff)
{
// store where to found the matching unique vertex
replaceIndex[a] = matchIndex | 0x80000000;
}
else
{
// no unique vertex matches it up to now -> so add it
replaceIndex[a] = (unsigned int)uniqueVertices.size();
uniqueVertices.push_back( v);
if (hasAnimMeshes) {
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex);
}
}
}
}
if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
ASSIMP_LOG_DEBUG_F(
"Mesh ",meshIndex,
" (",
(pMesh->mName.length ? pMesh->mName.data : "unnamed"),
") | Verts in: ",pMesh->mNumVertices,
" out: ",
uniqueVertices.size(),
" | ~",
((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f,
"%"
);
}
updateXMeshVertices(pMesh, uniqueVertices);
if (hasAnimMeshes) {
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]);
}
}
// adjust the indices in all faces
for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
{
aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++) {
face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
}
}
// adjust bone vertex weights.
for( int a = 0; a < (int)pMesh->mNumBones; a++) {
aiBone* bone = pMesh->mBones[a];
std::vector<aiVertexWeight> newWeights;
newWeights.reserve( bone->mNumWeights);
if ( NULL != bone->mWeights ) {
for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) {
const aiVertexWeight& ow = bone->mWeights[ b ];
// if the vertex is a unique one, translate it
if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) {
aiVertexWeight nw;
nw.mVertexId = replaceIndex[ ow.mVertexId ];
nw.mWeight = ow.mWeight;
newWeights.push_back( nw );
}
}
} else {
ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is NULL." );
}
if (newWeights.size() > 0) {
// kill the old and replace them with the translated weights
delete [] bone->mWeights;
bone->mNumWeights = (unsigned int)newWeights.size();
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
}
else {
/* NOTE:
*
* In the algorithm above we're assuming that there are no vertices
* with a different bone weight setup at the same position. That wouldn't
* make sense, but it is not absolutely impossible. SkeletonMeshBuilder
* for example generates such input data if two skeleton points
* share the same position. Again this doesn't make sense but is
* reality for some model formats (MD5 for example uses these special
* nodes as attachment tags for its weapons).
*
* Then it is possible that a bone has no weights anymore .... as a quick
* workaround, we're just removing these bones. If they're animated,
* model geometry might be modified but at least there's no risk of a crash.
*/
delete bone;
--pMesh->mNumBones;
for (unsigned int n = a; n < pMesh->mNumBones; ++n) {
pMesh->mBones[n] = pMesh->mBones[n+1];
}
--a;
ASSIMP_LOG_WARN("Removing bone -> no weights remaining");
}
}
return pMesh->mNumVertices;
}
#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS

View File

@ -0,0 +1,99 @@
/*
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 join identical vertices
on all imported meshes.*/
#ifndef AI_JOINVERTICESPROCESS_H_INC
#define AI_JOINVERTICESPROCESS_H_INC
#include "BaseProcess.h"
#include <assimp/types.h>
struct aiMesh;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** The JoinVerticesProcess unites identical vertices in all imported meshes.
* By default the importer returns meshes where each face addressed its own
* set of vertices even if that means that identical vertices are stored multiple
* times. The JoinVerticesProcess finds these identical vertices and
* erases all but one of the copies. This usually reduces the number of vertices
* in a mesh by a serious amount and is the standard form to render a mesh.
*/
class ASSIMP_API JoinVerticesProcess : public BaseProcess
{
public:
JoinVerticesProcess();
~JoinVerticesProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @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;
// -------------------------------------------------------------------
/** 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);
public:
// -------------------------------------------------------------------
/** Unites identical vertices in the given mesh.
* @param pMesh The mesh to process.
* @param meshIndex Index of the mesh to process
*/
int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
private:
};
} // end of namespace Assimp
#endif // AI_CALCTANGENTSPROCESS_H_INC

View File

@ -0,0 +1,201 @@
/*
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.
----------------------------------------------------------------------
*/
/** Implementation of the LimitBoneWeightsProcess post processing step */
#include "LimitBoneWeightsProcess.h"
#include <assimp/StringUtils.h>
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include <stdio.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
LimitBoneWeightsProcess::LimitBoneWeightsProcess()
{
mMaxWeights = AI_LMW_MAX_WEIGHTS;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
LimitBoneWeightsProcess::~LimitBoneWeightsProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_LimitBoneWeights) != 0;
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
ProcessMesh(pScene->mMeshes[a]);
}
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
{
// get the current value of the property
this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
}
// ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh
void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
{
if( !pMesh->HasBones())
return;
// collect all bone weights per vertex
typedef std::vector< std::vector< Weight > > WeightsPerVertex;
WeightsPerVertex vertexWeights( pMesh->mNumVertices);
// collect all weights per vertex
for( unsigned int a = 0; a < pMesh->mNumBones; a++)
{
const aiBone* bone = pMesh->mBones[a];
for( unsigned int b = 0; b < bone->mNumWeights; b++)
{
const aiVertexWeight& w = bone->mWeights[b];
vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
}
}
unsigned int removed = 0, old_bones = pMesh->mNumBones;
// now cut the weight count if it exceeds the maximum
bool bChanged = false;
for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
{
if( vit->size() <= mMaxWeights)
continue;
bChanged = true;
// more than the defined maximum -> first sort by weight in descending order. That's
// why we defined the < operator in such a weird way.
std::sort( vit->begin(), vit->end());
// now kill everything beyond the maximum count
unsigned int m = static_cast<unsigned int>(vit->size());
vit->erase( vit->begin() + mMaxWeights, vit->end());
removed += static_cast<unsigned int>(m-vit->size());
// and renormalize the weights
float sum = 0.0f;
for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) {
sum += it->mWeight;
}
if( 0.0f != sum ) {
const float invSum = 1.0f / sum;
for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) {
it->mWeight *= invSum;
}
}
}
if (bChanged) {
// rebuild the vertex weight array for all bones
typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
WeightsPerBone boneWeights( pMesh->mNumBones);
for( unsigned int a = 0; a < vertexWeights.size(); a++)
{
const std::vector<Weight>& vw = vertexWeights[a];
for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight));
}
// and finally copy the vertex weight list over to the mesh's bones
std::vector<bool> abNoNeed(pMesh->mNumBones,false);
bChanged = false;
for( unsigned int a = 0; a < pMesh->mNumBones; a++)
{
const std::vector<aiVertexWeight>& bw = boneWeights[a];
aiBone* bone = pMesh->mBones[a];
if ( bw.empty() )
{
abNoNeed[a] = bChanged = true;
continue;
}
// copy the weight list. should always be less weights than before, so we don't need a new allocation
ai_assert( bw.size() <= bone->mNumWeights);
bone->mNumWeights = static_cast<unsigned int>( bw.size() );
::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
}
if (bChanged) {
// the number of new bones is smaller than before, so we can reuse the old array
aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur;
for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
if (*iter) {
delete *ppcSrc;
--pMesh->mNumBones;
}
else *ppcCur++ = *ppcSrc;
++ppcSrc;
}
}
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones );
}
}
}

View File

@ -0,0 +1,148 @@
/*
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.
----------------------------------------------------------------------
*/
/** Defines a post processing step to limit the number of bones affecting a single vertex. */
#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
#define AI_LIMITBONEWEIGHTSPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
class LimitBoneWeightsTest;
namespace Assimp
{
// NOTE: If you change these limits, don't forget to change the
// corresponding values in all Assimp ports
// **********************************************************
// Java: ConfigProperty.java,
// ConfigProperty.DEFAULT_BONE_WEIGHT_LIMIT
// **********************************************************
#if (!defined AI_LMW_MAX_WEIGHTS)
# define AI_LMW_MAX_WEIGHTS 0x4
#endif // !! AI_LMW_MAX_WEIGHTS
// ---------------------------------------------------------------------------
/** This post processing step limits the number of bones affecting a vertex
* to a certain maximum value. If a vertex is affected by more than that number
* of bones, the bone weight with the least influence on this vertex are removed.
* The other weights on this bone are then renormalized to assure the sum weight
* to be 1.
*/
class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess
{
public:
LimitBoneWeightsProcess();
~LimitBoneWeightsProcess();
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.
*/
void SetupProperties(const Importer* pImp);
public:
// -------------------------------------------------------------------
/** Limits the bone weight count for all vertices in the given mesh.
* @param pMesh The mesh to process.
*/
void ProcessMesh( aiMesh* pMesh);
// -------------------------------------------------------------------
/** 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);
public:
// -------------------------------------------------------------------
/** Describes a bone weight on a vertex */
struct Weight
{
unsigned int mBone; ///< Index of the bone
float mWeight; ///< Weight of that bone on this vertex
Weight() AI_NO_EXCEPT
: mBone(0)
, mWeight(0.0f)
{ }
Weight( unsigned int pBone, float pWeight)
{
mBone = pBone;
mWeight = pWeight;
}
/** Comparison operator to sort bone weights by descending weight */
bool operator < (const Weight& pWeight) const
{
return mWeight > pWeight.mWeight;
}
};
public:
/** Maximum number of bones influencing any single vertex. */
unsigned int mMaxWeights;
};
} // end of namespace Assimp
#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC

83
thirdparty/assimp/code/MMDCpp14.h vendored Normal file
View File

@ -0,0 +1,83 @@
/*
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.
----------------------------------------------------------------------
*/
#pragma once
#ifndef MMD_CPP14_H
#define MMD_CPP14_H
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
namespace mmd {
template<class T> struct _Unique_if {
typedef std::unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]> {
typedef std::unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]> {
typedef void _Known_bound;
};
template<class T, class... Args>
typename _Unique_if<T>::_Single_object
make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T>
typename _Unique_if<T>::_Unknown_bound
make_unique(size_t n) {
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[n]());
}
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;
}
#endif

370
thirdparty/assimp/code/MMDImporter.cpp vendored Normal file
View File

@ -0,0 +1,370 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2016, 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 ASSIMP_BUILD_NO_MMD_IMPORTER
#include "MMDImporter.h"
#include "MMDPmdParser.h"
#include "MMDPmxParser.h"
#include "MMDVmdParser.h"
#include "ConvertToLHProcess.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp>
#include <assimp/ai_assert.h>
#include <assimp/scene.h>
#include <fstream>
#include <iomanip>
#include <memory>
static const aiImporterDesc desc = {"MMD Importer",
"",
"",
"surfaces supported?",
aiImporterFlags_SupportTextFlavour,
0,
0,
0,
0,
"pmx"};
namespace Assimp {
using namespace std;
// ------------------------------------------------------------------------------------------------
// Default constructor
MMDImporter::MMDImporter()
: m_Buffer()
, m_strAbsPath("") {
DefaultIOSystem io;
m_strAbsPath = io.getOsSeparator();
}
// ------------------------------------------------------------------------------------------------
// Destructor.
MMDImporter::~MMDImporter() {
// empty
}
// ------------------------------------------------------------------------------------------------
// Returns true, if file is an pmx file.
bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler,
bool checkSig) const {
if (!checkSig) // Check File Extension
{
return SimpleExtensionCheck(pFile, "pmx");
} else // Check file Header
{
static const char *pTokens[] = {"PMX "};
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1);
}
}
// ------------------------------------------------------------------------------------------------
const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; }
// ------------------------------------------------------------------------------------------------
// MMD import implementation
void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
IOSystem * /*pIOHandler*/) {
// Read file by istream
std::filebuf fb;
if (!fb.open(file, std::ios::in | std::ios::binary)) {
throw DeadlyImportError("Failed to open file " + file + ".");
}
std::istream fileStream(&fb);
// Get the file-size and validate it, throwing an exception when fails
fileStream.seekg(0, fileStream.end);
size_t fileSize = static_cast<size_t>(fileStream.tellg());
fileStream.seekg(0, fileStream.beg);
if (fileSize < sizeof(pmx::PmxModel)) {
throw DeadlyImportError(file + " is too small.");
}
pmx::PmxModel model;
model.Read(&fileStream);
CreateDataFromImport(&model, pScene);
}
// ------------------------------------------------------------------------------------------------
void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
aiScene *pScene) {
if (pModel == NULL) {
return;
}
aiNode *pNode = new aiNode;
if (!pModel->model_name.empty()) {
pNode->mName.Set(pModel->model_name);
}
pScene->mRootNode = pNode;
pNode = new aiNode;
pScene->mRootNode->addChildren(1, &pNode);
pNode->mName.Set(string(pModel->model_name) + string("_mesh"));
// split mesh by materials
pNode->mNumMeshes = pModel->material_count;
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
for (unsigned int index = 0; index < pNode->mNumMeshes; index++) {
pNode->mMeshes[index] = index;
}
pScene->mNumMeshes = pModel->material_count;
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) {
const int indexCount = pModel->materials[i].index_count;
pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount);
pScene->mMeshes[i]->mName = pModel->materials[i].material_name;
pScene->mMeshes[i]->mMaterialIndex = i;
indexStart += indexCount;
}
// create node hierarchy for bone position
std::unique_ptr<aiNode *[]> ppNode(new aiNode *[pModel->bone_count]);
for (auto i = 0; i < pModel->bone_count; i++) {
ppNode[i] = new aiNode(pModel->bones[i].bone_name);
}
for (auto i = 0; i < pModel->bone_count; i++) {
const pmx::PmxBone &bone = pModel->bones[i];
if (bone.parent_index < 0) {
pScene->mRootNode->addChildren(1, ppNode.get() + i);
} else {
ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i);
aiVector3D v3 = aiVector3D(
bone.position[0] - pModel->bones[bone.parent_index].position[0],
bone.position[1] - pModel->bones[bone.parent_index].position[1],
bone.position[2] - pModel->bones[bone.parent_index].position[2]);
aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation);
}
}
// create materials
pScene->mNumMaterials = pModel->material_count;
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials; i++) {
pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel);
}
// Convert everything to OpenGL space
MakeLeftHandedProcess convertProcess;
convertProcess.Execute(pScene);
FlipUVsProcess uvFlipper;
uvFlipper.Execute(pScene);
FlipWindingOrderProcess windingFlipper;
windingFlipper.Execute(pScene);
}
// ------------------------------------------------------------------------------------------------
aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
const int indexStart, const int indexCount) {
aiMesh *pMesh = new aiMesh;
pMesh->mNumVertices = indexCount;
pMesh->mNumFaces = indexCount / 3;
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
const int numIndices = 3; // triangular face
for (unsigned int index = 0; index < pMesh->mNumFaces; index++) {
pMesh->mFaces[index].mNumIndices = numIndices;
unsigned int *indices = new unsigned int[numIndices];
indices[0] = numIndices * index;
indices[1] = numIndices * index + 1;
indices[2] = numIndices * index + 2;
pMesh->mFaces[index].mIndices = indices;
}
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
pMesh->mNumUVComponents[0] = 2;
// additional UVs
for (int i = 1; i <= pModel->setting.uv; i++) {
pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices];
pMesh->mNumUVComponents[i] = 4;
}
map<int, vector<aiVertexWeight>> bone_vertex_map;
// fill in contents and create bones
for (int index = 0; index < indexCount; index++) {
const pmx::PmxVertex *v =
&pModel->vertices[pModel->indices[indexStart + index]];
const float *position = v->position;
pMesh->mVertices[index].Set(position[0], position[1], position[2]);
const float *normal = v->normal;
pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]);
pMesh->mTextureCoords[0][index].x = v->uv[0];
pMesh->mTextureCoords[0][index].y = v->uv[1];
for (int i = 1; i <= pModel->setting.uv; i++) {
// TODO: wrong here? use quaternion transform?
pMesh->mTextureCoords[i][index].x = v->uva[i][0];
pMesh->mTextureCoords[i][index].y = v->uva[i][1];
}
// handle bone map
const auto vsBDEF1_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF1 *>(v->skinning.get());
const auto vsBDEF2_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF2 *>(v->skinning.get());
const auto vsBDEF4_ptr =
dynamic_cast<pmx::PmxVertexSkinningBDEF4 *>(v->skinning.get());
const auto vsSDEF_ptr =
dynamic_cast<pmx::PmxVertexSkinningSDEF *>(v->skinning.get());
switch (v->skinning_type) {
case pmx::PmxVertexSkinningType::BDEF1:
bone_vertex_map[vsBDEF1_ptr->bone_index].push_back(
aiVertexWeight(index, 1.0));
break;
case pmx::PmxVertexSkinningType::BDEF2:
bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back(
aiVertexWeight(index, vsBDEF2_ptr->bone_weight));
bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back(
aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight));
break;
case pmx::PmxVertexSkinningType::BDEF4:
bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight1));
bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight2));
bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight3));
bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back(
aiVertexWeight(index, vsBDEF4_ptr->bone_weight4));
break;
case pmx::PmxVertexSkinningType::SDEF: // TODO: how to use sdef_c, sdef_r0,
// sdef_r1?
bone_vertex_map[vsSDEF_ptr->bone_index1].push_back(
aiVertexWeight(index, vsSDEF_ptr->bone_weight));
bone_vertex_map[vsSDEF_ptr->bone_index2].push_back(
aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight));
break;
case pmx::PmxVertexSkinningType::QDEF:
const auto vsQDEF_ptr =
dynamic_cast<pmx::PmxVertexSkinningQDEF *>(v->skinning.get());
bone_vertex_map[vsQDEF_ptr->bone_index1].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight1));
bone_vertex_map[vsQDEF_ptr->bone_index2].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight2));
bone_vertex_map[vsQDEF_ptr->bone_index3].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight3));
bone_vertex_map[vsQDEF_ptr->bone_index4].push_back(
aiVertexWeight(index, vsQDEF_ptr->bone_weight4));
break;
}
}
// make all bones for each mesh
// assign bone weights to skinned bones (otherwise just initialize)
auto bone_ptr_ptr = new aiBone *[pModel->bone_count];
pMesh->mNumBones = pModel->bone_count;
pMesh->mBones = bone_ptr_ptr;
for (auto ii = 0; ii < pModel->bone_count; ++ii) {
auto pBone = new aiBone;
const auto &pmxBone = pModel->bones[ii];
pBone->mName = pmxBone.bone_name;
aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]);
aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix);
auto it = bone_vertex_map.find(ii);
if (it != bone_vertex_map.end()) {
pBone->mNumWeights = static_cast<unsigned int>(it->second.size());
pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
for (unsigned int j = 0; j < pBone->mNumWeights; j++) {
pBone->mWeights[j] = it->second[j];
}
}
bone_ptr_ptr[ii] = pBone;
}
return pMesh;
}
// ------------------------------------------------------------------------------------------------
aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat,
const pmx::PmxModel *pModel) {
aiMaterial *mat = new aiMaterial();
aiString name(pMat->material_english_name);
mat->AddProperty(&name, AI_MATKEY_NAME);
aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]);
mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]);
mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR);
aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]);
mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT);
float opacity = pMat->diffuse[3];
mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY);
float shininess = pMat->specularlity;
mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH);
if(pMat->diffuse_texture_index >= 0) {
aiString texture_path(pModel->textures[pMat->diffuse_texture_index]);
mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
}
int mapping_uvwsrc = 0;
mat->AddProperty(&mapping_uvwsrc, 1,
AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0));
return mat;
}
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER

96
thirdparty/assimp/code/MMDImporter.h vendored Normal file
View File

@ -0,0 +1,96 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2016, 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 MMD_FILE_IMPORTER_H_INC
#define MMD_FILE_IMPORTER_H_INC
#include <assimp/BaseImporter.h>
#include "MMDPmxParser.h"
#include <assimp/material.h>
#include <vector>
struct aiMesh;
namespace Assimp {
// ------------------------------------------------------------------------------------------------
/// \class MMDImporter
/// \brief Imports MMD a pmx/pmd/vmd file
// ------------------------------------------------------------------------------------------------
class MMDImporter : public BaseImporter {
public:
/// \brief Default constructor
MMDImporter();
/// \brief Destructor
~MMDImporter();
public:
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
private:
//! \brief Appends the supported extension.
const aiImporterDesc* GetInfo () const;
//! \brief File import implementation.
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
//! \brief Create the data from imported content.
void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene);
//! \brief Create the mesh
aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount);
//! \brief Create the material
aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel);
private:
//! Data buffer
std::vector<char> m_Buffer;
//! Absolute pathname of model in file system
std::string m_strAbsPath;
};
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif

597
thirdparty/assimp/code/MMDPmdParser.h vendored Normal file
View File

@ -0,0 +1,597 @@
/*
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.
----------------------------------------------------------------------
*/
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <iostream>
#include <fstream>
#include "MMDCpp14.h"
namespace pmd
{
class PmdHeader
{
public:
std::string name;
std::string name_english;
std::string comment;
std::string comment_english;
bool Read(std::ifstream* stream)
{
char buffer[256];
stream->read(buffer, 20);
name = std::string(buffer);
stream->read(buffer, 256);
comment = std::string(buffer);
return true;
}
bool ReadExtension(std::ifstream* stream)
{
char buffer[256];
stream->read(buffer, 20);
name_english = std::string(buffer);
stream->read(buffer, 256);
comment_english = std::string(buffer);
return true;
}
};
class PmdVertex
{
public:
float position[3];
float normal[3];
float uv[2];
uint16_t bone_index[2];
uint8_t bone_weight;
bool edge_invisible;
bool Read(std::ifstream* stream)
{
stream->read((char*) position, sizeof(float) * 3);
stream->read((char*) normal, sizeof(float) * 3);
stream->read((char*) uv, sizeof(float) * 2);
stream->read((char*) bone_index, sizeof(uint16_t) * 2);
stream->read((char*) &bone_weight, sizeof(uint8_t));
stream->read((char*) &edge_invisible, sizeof(uint8_t));
return true;
}
};
class PmdMaterial
{
public:
float diffuse[4];
float power;
float specular[3];
float ambient[3];
uint8_t toon_index;
uint8_t edge_flag;
uint32_t index_count;
std::string texture_filename;
std::string sphere_filename;
bool Read(std::ifstream* stream)
{
char buffer[20];
stream->read((char*) &diffuse, sizeof(float) * 4);
stream->read((char*) &power, sizeof(float));
stream->read((char*) &specular, sizeof(float) * 3);
stream->read((char*) &ambient, sizeof(float) * 3);
stream->read((char*) &toon_index, sizeof(uint8_t));
stream->read((char*) &edge_flag, sizeof(uint8_t));
stream->read((char*) &index_count, sizeof(uint32_t));
stream->read((char*) &buffer, sizeof(char) * 20);
char* pstar = strchr(buffer, '*');
if (NULL == pstar)
{
texture_filename = std::string(buffer);
sphere_filename.clear();
}
else {
*pstar = 0;
texture_filename = std::string(buffer);
sphere_filename = std::string(pstar+1);
}
return true;
}
};
enum class BoneType : uint8_t
{
Rotation,
RotationAndMove,
IkEffector,
Unknown,
IkEffectable,
RotationEffectable,
IkTarget,
Invisible,
Twist,
RotationMovement
};
class PmdBone
{
public:
std::string name;
std::string name_english;
uint16_t parent_bone_index;
uint16_t tail_pos_bone_index;
BoneType bone_type;
uint16_t ik_parent_bone_index;
float bone_head_pos[3];
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name = std::string(buffer);
stream->read((char*) &parent_bone_index, sizeof(uint16_t));
stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t));
stream->read((char*) &bone_type, sizeof(uint8_t));
stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t));
stream->read((char*) &bone_head_pos, sizeof(float) * 3);
}
void ReadExpantion(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name_english = std::string(buffer);
}
};
class PmdIk
{
public:
uint16_t ik_bone_index;
uint16_t target_bone_index;
uint16_t interations;
float angle_limit;
std::vector<uint16_t> ik_child_bone_index;
void Read(std::istream *stream)
{
stream->read((char *) &ik_bone_index, sizeof(uint16_t));
stream->read((char *) &target_bone_index, sizeof(uint16_t));
uint8_t ik_chain_length;
stream->read((char*) &ik_chain_length, sizeof(uint8_t));
stream->read((char *) &interations, sizeof(uint16_t));
stream->read((char *) &angle_limit, sizeof(float));
ik_child_bone_index.resize(ik_chain_length);
for (int i = 0; i < ik_chain_length; i++)
{
stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t));
}
}
};
class PmdFaceVertex
{
public:
int vertex_index;
float position[3];
void Read(std::istream *stream)
{
stream->read((char *) &vertex_index, sizeof(int));
stream->read((char *) position, sizeof(float) * 3);
}
};
enum class FaceCategory : uint8_t
{
Base,
Eyebrow,
Eye,
Mouth,
Other
};
class PmdFace
{
public:
std::string name;
FaceCategory type;
std::vector<PmdFaceVertex> vertices;
std::string name_english;
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name = std::string(buffer);
int vertex_count;
stream->read((char*) &vertex_count, sizeof(int));
stream->read((char*) &type, sizeof(uint8_t));
vertices.resize(vertex_count);
for (int i = 0; i < vertex_count; i++)
{
vertices[i].Read(stream);
}
}
void ReadExpantion(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name_english = std::string(buffer);
}
};
class PmdBoneDispName
{
public:
std::string bone_disp_name;
std::string bone_disp_name_english;
void Read(std::istream *stream)
{
char buffer[50];
stream->read(buffer, 50);
bone_disp_name = std::string(buffer);
bone_disp_name_english.clear();
}
void ReadExpantion(std::istream *stream)
{
char buffer[50];
stream->read(buffer, 50);
bone_disp_name_english = std::string(buffer);
}
};
class PmdBoneDisp
{
public:
uint16_t bone_index;
uint8_t bone_disp_index;
void Read(std::istream *stream)
{
stream->read((char*) &bone_index, sizeof(uint16_t));
stream->read((char*) &bone_disp_index, sizeof(uint8_t));
}
};
enum class RigidBodyShape : uint8_t
{
Sphere = 0,
Box = 1,
Cpusel = 2
};
enum class RigidBodyType : uint8_t
{
BoneConnected = 0,
Physics = 1,
ConnectedPhysics = 2
};
class PmdRigidBody
{
public:
std::string name;
uint16_t related_bone_index;
uint8_t group_index;
uint16_t mask;
RigidBodyShape shape;
float size[3];
float position[3];
float orientation[3];
float weight;
float linear_damping;
float anglar_damping;
float restitution;
float friction;
RigidBodyType rigid_type;
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, sizeof(char) * 20);
name = (std::string(buffer));
stream->read((char*) &related_bone_index, sizeof(uint16_t));
stream->read((char*) &group_index, sizeof(uint8_t));
stream->read((char*) &mask, sizeof(uint16_t));
stream->read((char*) &shape, sizeof(uint8_t));
stream->read((char*) size, sizeof(float) * 3);
stream->read((char*) position, sizeof(float) * 3);
stream->read((char*) orientation, sizeof(float) * 3);
stream->read((char*) &weight, sizeof(float));
stream->read((char*) &linear_damping, sizeof(float));
stream->read((char*) &anglar_damping, sizeof(float));
stream->read((char*) &restitution, sizeof(float));
stream->read((char*) &friction, sizeof(float));
stream->read((char*) &rigid_type, sizeof(char));
}
};
class PmdConstraint
{
public:
std::string name;
uint32_t rigid_body_index_a;
uint32_t rigid_body_index_b;
float position[3];
float orientation[3];
float linear_lower_limit[3];
float linear_upper_limit[3];
float angular_lower_limit[3];
float angular_upper_limit[3];
float linear_stiffness[3];
float angular_stiffness[3];
void Read(std::istream *stream)
{
char buffer[20];
stream->read(buffer, 20);
name = std::string(buffer);
stream->read((char *) &rigid_body_index_a, sizeof(uint32_t));
stream->read((char *) &rigid_body_index_b, sizeof(uint32_t));
stream->read((char *) position, sizeof(float) * 3);
stream->read((char *) orientation, sizeof(float) * 3);
stream->read((char *) linear_lower_limit, sizeof(float) * 3);
stream->read((char *) linear_upper_limit, sizeof(float) * 3);
stream->read((char *) angular_lower_limit, sizeof(float) * 3);
stream->read((char *) angular_upper_limit, sizeof(float) * 3);
stream->read((char *) linear_stiffness, sizeof(float) * 3);
stream->read((char *) angular_stiffness, sizeof(float) * 3);
}
};
class PmdModel
{
public:
float version;
PmdHeader header;
std::vector<PmdVertex> vertices;
std::vector<uint16_t> indices;
std::vector<PmdMaterial> materials;
std::vector<PmdBone> bones;
std::vector<PmdIk> iks;
std::vector<PmdFace> faces;
std::vector<uint16_t> faces_indices;
std::vector<PmdBoneDispName> bone_disp_name;
std::vector<PmdBoneDisp> bone_disp;
std::vector<std::string> toon_filenames;
std::vector<PmdRigidBody> rigid_bodies;
std::vector<PmdConstraint> constraints;
static std::unique_ptr<PmdModel> LoadFromFile(const char *filename)
{
std::ifstream stream(filename, std::ios::binary);
if (stream.fail())
{
std::cerr << "could not open \"" << filename << "\"" << std::endl;
return nullptr;
}
auto result = LoadFromStream(&stream);
stream.close();
return result;
}
static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream)
{
auto result = mmd::make_unique<PmdModel>();
char buffer[100];
// magic
char magic[3];
stream->read(magic, 3);
if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd')
{
std::cerr << "invalid file" << std::endl;
return nullptr;
}
// version
stream->read((char*) &(result->version), sizeof(float));
if (result ->version != 1.0f)
{
std::cerr << "invalid version" << std::endl;
return nullptr;
}
// header
result->header.Read(stream);
// vertices
uint32_t vertex_num;
stream->read((char*) &vertex_num, sizeof(uint32_t));
result->vertices.resize(vertex_num);
for (uint32_t i = 0; i < vertex_num; i++)
{
result->vertices[i].Read(stream);
}
// indices
uint32_t index_num;
stream->read((char*) &index_num, sizeof(uint32_t));
result->indices.resize(index_num);
for (uint32_t i = 0; i < index_num; i++)
{
stream->read((char*) &result->indices[i], sizeof(uint16_t));
}
// materials
uint32_t material_num;
stream->read((char*) &material_num, sizeof(uint32_t));
result->materials.resize(material_num);
for (uint32_t i = 0; i < material_num; i++)
{
result->materials[i].Read(stream);
}
// bones
uint16_t bone_num;
stream->read((char*) &bone_num, sizeof(uint16_t));
result->bones.resize(bone_num);
for (uint32_t i = 0; i < bone_num; i++)
{
result->bones[i].Read(stream);
}
// iks
uint16_t ik_num;
stream->read((char*) &ik_num, sizeof(uint16_t));
result->iks.resize(ik_num);
for (uint32_t i = 0; i < ik_num; i++)
{
result->iks[i].Read(stream);
}
// faces
uint16_t face_num;
stream->read((char*) &face_num, sizeof(uint16_t));
result->faces.resize(face_num);
for (uint32_t i = 0; i < face_num; i++)
{
result->faces[i].Read(stream);
}
// face frames
uint8_t face_frame_num;
stream->read((char*) &face_frame_num, sizeof(uint8_t));
result->faces_indices.resize(face_frame_num);
for (uint32_t i = 0; i < face_frame_num; i++)
{
stream->read((char*) &result->faces_indices[i], sizeof(uint16_t));
}
// bone names
uint8_t bone_disp_num;
stream->read((char*) &bone_disp_num, sizeof(uint8_t));
result->bone_disp_name.resize(bone_disp_num);
for (uint32_t i = 0; i < bone_disp_num; i++)
{
result->bone_disp_name[i].Read(stream);
}
// bone frame
uint32_t bone_frame_num;
stream->read((char*) &bone_frame_num, sizeof(uint32_t));
result->bone_disp.resize(bone_frame_num);
for (uint32_t i = 0; i < bone_frame_num; i++)
{
result->bone_disp[i].Read(stream);
}
// english name
bool english;
stream->read((char*) &english, sizeof(char));
if (english)
{
result->header.ReadExtension(stream);
for (uint32_t i = 0; i < bone_num; i++)
{
result->bones[i].ReadExpantion(stream);
}
for (uint32_t i = 0; i < face_num; i++)
{
if (result->faces[i].type == pmd::FaceCategory::Base)
{
continue;
}
result->faces[i].ReadExpantion(stream);
}
for (uint32_t i = 0; i < result->bone_disp_name.size(); i++)
{
result->bone_disp_name[i].ReadExpantion(stream);
}
}
// toon textures
if (stream->peek() == std::ios::traits_type::eof())
{
result->toon_filenames.clear();
}
else {
result->toon_filenames.resize(10);
for (uint32_t i = 0; i < 10; i++)
{
stream->read(buffer, 100);
result->toon_filenames[i] = std::string(buffer);
}
}
// physics
if (stream->peek() == std::ios::traits_type::eof())
{
result->rigid_bodies.clear();
result->constraints.clear();
}
else {
uint32_t rigid_body_num;
stream->read((char*) &rigid_body_num, sizeof(uint32_t));
result->rigid_bodies.resize(rigid_body_num);
for (uint32_t i = 0; i < rigid_body_num; i++)
{
result->rigid_bodies[i].Read(stream);
}
uint32_t constraint_num;
stream->read((char*) &constraint_num, sizeof(uint32_t));
result->constraints.resize(constraint_num);
for (uint32_t i = 0; i < constraint_num; i++)
{
result->constraints[i].Read(stream);
}
}
if (stream->peek() != std::ios::traits_type::eof())
{
std::cerr << "there is unknown data" << std::endl;
}
return result;
}
};
}

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