From 65255f132183f0e23276e85a26c617dec6a3d30f Mon Sep 17 00:00:00 2001 From: iDunnoDev Date: Fri, 18 Mar 2022 21:26:31 +0000 Subject: [PATCH] Added the ASSIMP library Added the files provided for the tutorial Added the SplitMeshNode and SubMeshNode classes --- Assimp/include/assimp/.editorconfig | 8 + Assimp/include/assimp/Compiler/poppack1.h | 22 + Assimp/include/assimp/Compiler/pstdint.h | 912 ++++++++++ Assimp/include/assimp/Compiler/pushpack1.h | 43 + Assimp/include/assimp/DefaultIOStream.h | 141 ++ Assimp/include/assimp/DefaultIOSystem.h | 99 ++ Assimp/include/assimp/DefaultLogger.hpp | 191 ++ Assimp/include/assimp/Defines.h | 49 + Assimp/include/assimp/Exporter.hpp | 483 +++++ Assimp/include/assimp/IOStream.hpp | 140 ++ Assimp/include/assimp/IOSystem.hpp | 359 ++++ Assimp/include/assimp/Importer.hpp | 653 +++++++ Assimp/include/assimp/LogStream.hpp | 110 ++ Assimp/include/assimp/Logger.hpp | 268 +++ Assimp/include/assimp/NullLogger.hpp | 98 ++ Assimp/include/assimp/ProgressHandler.hpp | 126 ++ Assimp/include/assimp/SceneCombiner.h | 400 +++++ Assimp/include/assimp/ai_assert.h | 53 + Assimp/include/assimp/anim.h | 572 ++++++ Assimp/include/assimp/camera.h | 225 +++ Assimp/include/assimp/cexport.h | 263 +++ Assimp/include/assimp/cfileio.h | 137 ++ Assimp/include/assimp/cimport.h | 564 ++++++ Assimp/include/assimp/color4.h | 104 ++ Assimp/include/assimp/color4.inl | 204 +++ Assimp/include/assimp/config.h | 963 ++++++++++ Assimp/include/assimp/config.h.in | 963 ++++++++++ Assimp/include/assimp/defs.h | 290 +++ Assimp/include/assimp/importerdesc.h | 145 ++ Assimp/include/assimp/light.h | 258 +++ Assimp/include/assimp/material.h | 1564 +++++++++++++++++ Assimp/include/assimp/material.inl | 389 ++++ Assimp/include/assimp/matrix3x3.h | 182 ++ Assimp/include/assimp/matrix3x3.inl | 356 ++++ Assimp/include/assimp/matrix4x4.h | 279 +++ Assimp/include/assimp/matrix4x4.inl | 681 +++++++ Assimp/include/assimp/mesh.h | 773 ++++++++ Assimp/include/assimp/metadata.h | 314 ++++ .../port/AndroidJNI/AndroidJNIIOSystem.h | 92 + Assimp/include/assimp/postprocess.h | 645 +++++++ Assimp/include/assimp/quaternion.h | 129 ++ Assimp/include/assimp/quaternion.inl | 285 +++ Assimp/include/assimp/scene.h | 384 ++++ Assimp/include/assimp/texture.h | 217 +++ Assimp/include/assimp/types.h | 518 ++++++ Assimp/include/assimp/vector2.h | 116 ++ Assimp/include/assimp/vector2.inl | 227 +++ Assimp/include/assimp/vector3.h | 145 ++ Assimp/include/assimp/vector3.inl | 260 +++ Assimp/include/assimp/version.h | 108 ++ Graphics2/DirectXFramework.cpp | 5 +- Graphics2/DirectXFramework.h | 5 + Graphics2/Graphics2.cpp | 31 +- Graphics2/Graphics2.h | 9 + Graphics2/Graphics2.vcxproj | 42 + Graphics2/Graphics2.vcxproj.filters | 96 + Graphics2/Mesh.cpp | 70 + Graphics2/Mesh.h | 92 + Graphics2/MeshNode.cpp | 29 + Graphics2/MeshNode.h | 22 + Graphics2/MeshRenderer.cpp | 234 +++ Graphics2/MeshRenderer.h | 58 + Graphics2/Models/Plane/Bonanza.3ds | Bin 0 -> 581572 bytes Graphics2/Models/Plane/Defaul.jpg | Bin 0 -> 631 bytes Graphics2/Models/Plane/anthra.jpg | Bin 0 -> 631 bytes Graphics2/Models/Plane/fin.jpg | Bin 0 -> 75386 bytes Graphics2/Models/Plane/fin1.jpg | Bin 0 -> 19969 bytes Graphics2/Models/Plane/fus.jpg | Bin 0 -> 121934 bytes Graphics2/Models/Plane/fus1.jpg | Bin 0 -> 18136 bytes Graphics2/Models/Plane/lwin.jpg | Bin 0 -> 104087 bytes Graphics2/Models/Plane/lwin1.jpg | Bin 0 -> 23770 bytes Graphics2/Models/Plane/nose.jpg | Bin 0 -> 22896 bytes Graphics2/Models/Plane/panel.jpg | Bin 0 -> 155905 bytes Graphics2/Models/Plane/red.jpg | Bin 0 -> 633 bytes Graphics2/Models/Plane/rwin.jpg | Bin 0 -> 102207 bytes Graphics2/Models/Plane/rwin1.jpg | Bin 0 -> 23770 bytes Graphics2/Models/Plane/tail.jpg | Bin 0 -> 631 bytes Graphics2/Models/Plane/tail1.jpg | Bin 0 -> 23139 bytes Graphics2/Renderer.h | 13 + Graphics2/ResourceManager.cpp | 479 +++++ Graphics2/ResourceManager.h | 65 + Graphics2/SceneGraph.cpp | 80 + Graphics2/SceneGraph.h | 15 +- Graphics2/SceneNode.cpp | 0 Graphics2/SceneNode.h | 3 + Graphics2/SharedMethods.cpp | 7 + Graphics2/SharedMethods.h | 23 + Graphics2/SplitMeshNode.cpp | 63 + Graphics2/SplitMeshNode.h | 22 + Graphics2/SubMeshNode.cpp | 26 + Graphics2/SubMeshNode.h | 21 + Graphics2/SubMeshRenderer.cpp | 203 +++ Graphics2/SubMeshRenderer.h | 56 + Graphics2/TexturedCubeNode.cpp | 248 +++ Graphics2/TexturedCubeNode.h | 39 + Graphics2/TexturedShaders.hlsl | 70 + Graphics2/Textures/Bricks.png | Bin 0 -> 36200 bytes Graphics2/Textures/Concrete.png | Bin 0 -> 25530 bytes Graphics2/Textures/Wood.png | Bin 0 -> 299871 bytes Graphics2/Textures/white.png | Bin 0 -> 160 bytes Graphics2/Textures/woodbox.bmp | Bin 0 -> 196662 bytes Graphics2/WICTextureLoader.cpp | 934 ++++++++++ Graphics2/WICTextureLoader.h | 130 ++ Graphics2/shader.hlsl | 51 + Graphics2/treeTest.txt | 79 + 105 files changed, 19816 insertions(+), 11 deletions(-) create mode 100644 Assimp/include/assimp/.editorconfig create mode 100644 Assimp/include/assimp/Compiler/poppack1.h create mode 100644 Assimp/include/assimp/Compiler/pstdint.h create mode 100644 Assimp/include/assimp/Compiler/pushpack1.h create mode 100644 Assimp/include/assimp/DefaultIOStream.h create mode 100644 Assimp/include/assimp/DefaultIOSystem.h create mode 100644 Assimp/include/assimp/DefaultLogger.hpp create mode 100644 Assimp/include/assimp/Defines.h create mode 100644 Assimp/include/assimp/Exporter.hpp create mode 100644 Assimp/include/assimp/IOStream.hpp create mode 100644 Assimp/include/assimp/IOSystem.hpp create mode 100644 Assimp/include/assimp/Importer.hpp create mode 100644 Assimp/include/assimp/LogStream.hpp create mode 100644 Assimp/include/assimp/Logger.hpp create mode 100644 Assimp/include/assimp/NullLogger.hpp create mode 100644 Assimp/include/assimp/ProgressHandler.hpp create mode 100644 Assimp/include/assimp/SceneCombiner.h create mode 100644 Assimp/include/assimp/ai_assert.h create mode 100644 Assimp/include/assimp/anim.h create mode 100644 Assimp/include/assimp/camera.h create mode 100644 Assimp/include/assimp/cexport.h create mode 100644 Assimp/include/assimp/cfileio.h create mode 100644 Assimp/include/assimp/cimport.h create mode 100644 Assimp/include/assimp/color4.h create mode 100644 Assimp/include/assimp/color4.inl create mode 100644 Assimp/include/assimp/config.h create mode 100644 Assimp/include/assimp/config.h.in create mode 100644 Assimp/include/assimp/defs.h create mode 100644 Assimp/include/assimp/importerdesc.h create mode 100644 Assimp/include/assimp/light.h create mode 100644 Assimp/include/assimp/material.h create mode 100644 Assimp/include/assimp/material.inl create mode 100644 Assimp/include/assimp/matrix3x3.h create mode 100644 Assimp/include/assimp/matrix3x3.inl create mode 100644 Assimp/include/assimp/matrix4x4.h create mode 100644 Assimp/include/assimp/matrix4x4.inl create mode 100644 Assimp/include/assimp/mesh.h create mode 100644 Assimp/include/assimp/metadata.h create mode 100644 Assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h create mode 100644 Assimp/include/assimp/postprocess.h create mode 100644 Assimp/include/assimp/quaternion.h create mode 100644 Assimp/include/assimp/quaternion.inl create mode 100644 Assimp/include/assimp/scene.h create mode 100644 Assimp/include/assimp/texture.h create mode 100644 Assimp/include/assimp/types.h create mode 100644 Assimp/include/assimp/vector2.h create mode 100644 Assimp/include/assimp/vector2.inl create mode 100644 Assimp/include/assimp/vector3.h create mode 100644 Assimp/include/assimp/vector3.inl create mode 100644 Assimp/include/assimp/version.h create mode 100644 Graphics2/Mesh.cpp create mode 100644 Graphics2/Mesh.h create mode 100644 Graphics2/MeshNode.cpp create mode 100644 Graphics2/MeshNode.h create mode 100644 Graphics2/MeshRenderer.cpp create mode 100644 Graphics2/MeshRenderer.h create mode 100644 Graphics2/Models/Plane/Bonanza.3ds create mode 100644 Graphics2/Models/Plane/Defaul.jpg create mode 100644 Graphics2/Models/Plane/anthra.jpg create mode 100644 Graphics2/Models/Plane/fin.jpg create mode 100644 Graphics2/Models/Plane/fin1.jpg create mode 100644 Graphics2/Models/Plane/fus.jpg create mode 100644 Graphics2/Models/Plane/fus1.jpg create mode 100644 Graphics2/Models/Plane/lwin.jpg create mode 100644 Graphics2/Models/Plane/lwin1.jpg create mode 100644 Graphics2/Models/Plane/nose.jpg create mode 100644 Graphics2/Models/Plane/panel.jpg create mode 100644 Graphics2/Models/Plane/red.jpg create mode 100644 Graphics2/Models/Plane/rwin.jpg create mode 100644 Graphics2/Models/Plane/rwin1.jpg create mode 100644 Graphics2/Models/Plane/tail.jpg create mode 100644 Graphics2/Models/Plane/tail1.jpg create mode 100644 Graphics2/Renderer.h create mode 100644 Graphics2/ResourceManager.cpp create mode 100644 Graphics2/ResourceManager.h create mode 100644 Graphics2/SceneGraph.cpp create mode 100644 Graphics2/SceneNode.cpp create mode 100644 Graphics2/SharedMethods.cpp create mode 100644 Graphics2/SharedMethods.h create mode 100644 Graphics2/SplitMeshNode.cpp create mode 100644 Graphics2/SplitMeshNode.h create mode 100644 Graphics2/SubMeshNode.cpp create mode 100644 Graphics2/SubMeshNode.h create mode 100644 Graphics2/SubMeshRenderer.cpp create mode 100644 Graphics2/SubMeshRenderer.h create mode 100644 Graphics2/TexturedCubeNode.cpp create mode 100644 Graphics2/TexturedCubeNode.h create mode 100644 Graphics2/TexturedShaders.hlsl create mode 100644 Graphics2/Textures/Bricks.png create mode 100644 Graphics2/Textures/Concrete.png create mode 100644 Graphics2/Textures/Wood.png create mode 100644 Graphics2/Textures/white.png create mode 100644 Graphics2/Textures/woodbox.bmp create mode 100644 Graphics2/WICTextureLoader.cpp create mode 100644 Graphics2/WICTextureLoader.h create mode 100644 Graphics2/shader.hlsl create mode 100644 Graphics2/treeTest.txt diff --git a/Assimp/include/assimp/.editorconfig b/Assimp/include/assimp/.editorconfig new file mode 100644 index 0000000..9ea6642 --- /dev/null +++ b/Assimp/include/assimp/.editorconfig @@ -0,0 +1,8 @@ +# See for details + +[*.{h,hpp,inl}] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space diff --git a/Assimp/include/assimp/Compiler/poppack1.h b/Assimp/include/assimp/Compiler/poppack1.h new file mode 100644 index 0000000..e033bc1 --- /dev/null +++ b/Assimp/include/assimp/Compiler/poppack1.h @@ -0,0 +1,22 @@ + +// =============================================================================== +// May be included multiple times - resets structure packing to the defaults +// for all supported compilers. Reverts the changes made by #include +// +// Currently this works on the following compilers: +// MSVC 7,8,9 +// GCC +// BORLAND (complains about 'pack state changed but not reverted', but works) +// =============================================================================== + +#ifndef AI_PUSHPACK_IS_DEFINED +# error pushpack1.h must be included after poppack1.h +#endif + +// reset packing to the original value +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop ) +#endif +#undef PACK_STRUCT + +#undef AI_PUSHPACK_IS_DEFINED diff --git a/Assimp/include/assimp/Compiler/pstdint.h b/Assimp/include/assimp/Compiler/pstdint.h new file mode 100644 index 0000000..4de4ce2 --- /dev/null +++ b/Assimp/include/assimp/Compiler/pstdint.h @@ -0,0 +1,912 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2016 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + **************************************************************************** + * + * Version 0.1.15.4 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that some compiler vendors chose to ignore the C99 + * standard and some older compilers have no opportunity to be updated. + * Because of this situation, simply including stdint.h in your code + * makes it unportable. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. Even compilers + * that already come with stdint.h can use this file instead without + * any loss of functionality. A few things that should be noted about + * this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current version, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * PRINTF_UINTMAX_DEC_WIDTH + * PRINTF_UINT64_DEC_WIDTH + * PRINTF_UINT32_DEC_WIDTH + * PRINTF_UINT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * Florian Wobbe + * Christopher Sean Morrison + * Mikkel Fahnoe Jorgensen + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__)) +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "l" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# else +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# if (UINT_MAX == UINT32_MAX) +# define PRINTF_INT32_MODIFIER "" +# else +# define PRINTF_INT32_MODIFIER "l" +# endif +# endif +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_UINT64_HEX_WIDTH +# define PRINTF_UINT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_UINT32_HEX_WIDTH +# define PRINTF_UINT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_UINT16_HEX_WIDTH +# define PRINTF_UINT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_UINT8_HEX_WIDTH +# define PRINTF_UINT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +# endif +# ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_UINTMAX_HEX_WIDTH +# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif +# ifndef PRINTF_UINTMAX_DEC_WIDTH +# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +/* + * I have no idea what is the truly correct thing to do on older Solaris. + * From some online discussions, this seems to be what is being + * recommended. For people who actually are developing on older Solaris, + * what I would like to know is, does this define all of the relevant + * macros of a complete stdint.h? Remember, in pstdint.h 64 bit is + * considered optional. + */ + +#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks) +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks) +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks) +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks) +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks) +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks) +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) && !defined(vxWorks) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif +#ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# else +/* #error "This platform might not be supported yet" */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are + * not defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits) + +#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; } + +int main () { + int err_n = 0; + int err_first = 0; + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); + if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); + sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); + if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); + if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); + if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); +#endif + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + +#define STR(v) #v +#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v)); + if (err_n) { + printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); + } + + Q(int) + Q(unsigned) + Q(long int) + Q(short int) + Q(int8_t) + Q(int16_t) + Q(int32_t) +#ifdef INT64_MAX + Q(int64_t) +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/Assimp/include/assimp/Compiler/pushpack1.h b/Assimp/include/assimp/Compiler/pushpack1.h new file mode 100644 index 0000000..94ee1e4 --- /dev/null +++ b/Assimp/include/assimp/Compiler/pushpack1.h @@ -0,0 +1,43 @@ + + +// =============================================================================== +// May be included multiple times - sets structure packing to 1 +// for all supported compilers. #include reverts the changes. +// +// Currently this works on the following compilers: +// MSVC 7,8,9 +// GCC +// BORLAND (complains about 'pack state changed but not reverted', but works) +// Clang +// +// +// USAGE: +// +// struct StructToBePacked { +// } PACK_STRUCT; +// +// =============================================================================== + +#ifdef AI_PUSHPACK_IS_DEFINED +# error poppack1.h must be included after pushpack1.h +#endif + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack(push,1) +# define PACK_STRUCT +#elif defined( __GNUC__ ) +# if !defined(HOST_MINGW) +# define PACK_STRUCT __attribute__((__packed__)) +# else +# define PACK_STRUCT __attribute__((gcc_struct, __packed__)) +# endif +#else +# error Compiler not supported +#endif + +#if defined(_MSC_VER) +// C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop +# pragma warning (disable : 4103) +#endif + +#define AI_PUSHPACK_IS_DEFINED diff --git a/Assimp/include/assimp/DefaultIOStream.h b/Assimp/include/assimp/DefaultIOStream.h new file mode 100644 index 0000000..3668b27 --- /dev/null +++ b/Assimp/include/assimp/DefaultIOStream.h @@ -0,0 +1,141 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 file I/O using fXXX()-family of functions */ +#ifndef AI_DEFAULTIOSTREAM_H_INC +#define AI_DEFAULTIOSTREAM_H_INC + +#include +#include +#include +#include + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +//! @class DefaultIOStream +//! @brief Default IO implementation, use standard IO operations +//! @note An instance of this class can exist without a valid file handle +//! attached to it. All calls fail, but the instance can nevertheless be +//! used with no restrictions. +class ASSIMP_API DefaultIOStream : public IOStream +{ + friend class DefaultIOSystem; +#if __ANDROID__ +# if __ANDROID_API__ > 9 +# if defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) + friend class AndroidJNIIOSystem; +# endif // defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) +# endif // __ANDROID_API__ > 9 +#endif // __ANDROID__ + +protected: + DefaultIOStream(); + DefaultIOStream(FILE* pFile, const std::string &strFilename); + +public: + /** Destructor public to allow simple deletion to close the file. */ + ~DefaultIOStream (); + + // ------------------------------------------------------------------- + /// Read from stream + size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount); + + + // ------------------------------------------------------------------- + /// Write to stream + size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount); + + // ------------------------------------------------------------------- + /// Seek specific position + aiReturn Seek(size_t pOffset, + aiOrigin pOrigin); + + // ------------------------------------------------------------------- + /// Get current seek position + size_t Tell() const; + + // ------------------------------------------------------------------- + /// Get size of file + size_t FileSize() const; + + // ------------------------------------------------------------------- + /// Flush file contents + void Flush(); + +private: + // File data-structure, using clib + FILE* mFile; + // Filename + std::string mFilename; + + // Cached file size + mutable size_t mCachedSize; +}; + +// ---------------------------------------------------------------------------------- +inline DefaultIOStream::DefaultIOStream () : + mFile (NULL), + mFilename (""), + mCachedSize(SIZE_MAX) +{ + // empty +} + +// ---------------------------------------------------------------------------------- +inline DefaultIOStream::DefaultIOStream (FILE* pFile, + const std::string &strFilename) : + mFile(pFile), + mFilename(strFilename), + mCachedSize(SIZE_MAX) +{ + // empty +} +// ---------------------------------------------------------------------------------- + +} // ns assimp + +#endif //!!AI_DEFAULTIOSTREAM_H_INC + diff --git a/Assimp/include/assimp/DefaultIOSystem.h b/Assimp/include/assimp/DefaultIOSystem.h new file mode 100644 index 0000000..d7cf031 --- /dev/null +++ b/Assimp/include/assimp/DefaultIOSystem.h @@ -0,0 +1,99 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 */ +#ifndef AI_DEFAULTIOSYSTEM_H_INC +#define AI_DEFAULTIOSYSTEM_H_INC + +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Default implementation of IOSystem using the standard C file functions */ +class ASSIMP_API DefaultIOSystem : public IOSystem +{ +public: + /** Constructor. */ + DefaultIOSystem(); + + /** Destructor. */ + ~DefaultIOSystem(); + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const; + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const; + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb"); + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile); + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const; + + /** @brief get the file name of a full filepath + * example: /tmp/archive.tar.gz -> archive.tar.gz + */ + static std::string fileName( const std::string &path ); + + /** @brief get the complete base name of a full filepath + * example: /tmp/archive.tar.gz -> archive.tar + */ + static std::string completeBaseName( const std::string &path); + + /** @brief get the path of a full filepath + * example: /tmp/archive.tar.gz -> /tmp/ + */ + static std::string absolutePath( const std::string &path); +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/Assimp/include/assimp/DefaultLogger.hpp b/Assimp/include/assimp/DefaultLogger.hpp new file mode 100644 index 0000000..4f1a7e1 --- /dev/null +++ b/Assimp/include/assimp/DefaultLogger.hpp @@ -0,0 +1,191 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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.hpp +*/ + +#ifndef INCLUDED_AI_DEFAULTLOGGER +#define INCLUDED_AI_DEFAULTLOGGER + +#include "Logger.hpp" +#include "LogStream.hpp" +#include "NullLogger.hpp" +#include + +namespace Assimp { +// ------------------------------------------------------------------------------------ +class IOStream; +struct LogStreamInfo; + +/** default name of logfile */ +#define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt" + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Primary logging facility of Assimp. + * + * The library stores its primary #Logger as a static member of this class. + * #get() returns this primary logger. By default the underlying implementation is + * just a #NullLogger which rejects all log messages. By calling #create(), logging + * is turned on. To capture the log output multiple log streams (#LogStream) can be + * attach to the logger. Some default streams for common streaming locations (such as + * a file, std::cout, OutputDebugString()) are also provided. + * + * If you wish to customize the logging at an even deeper level supply your own + * implementation of #Logger to #set(). + * @note The whole logging stuff causes a small extra overhead for all imports. */ +class ASSIMP_API DefaultLogger : + public Logger { + +public: + + // ---------------------------------------------------------------------- + /** @brief Creates a logging instance. + * @param name Name for log file. Only valid in combination + * with the aiDefaultLogStream_FILE flag. + * @param severity Log severity, VERBOSE turns on debug messages + * @param defStreams Default log streams to be attached. Any bitwise + * combination of the aiDefaultLogStream enumerated values. + * If #aiDefaultLogStream_FILE is specified but an empty string is + * passed for 'name', no log file is created at all. + * @param io IOSystem to be used to open external files (such as the + * log file). Pass NULL to rely on the default implementation. + * This replaces the default #NullLogger with a #DefaultLogger instance. */ + static Logger *create(const char* name = ASSIMP_DEFAULT_LOG_NAME, + LogSeverity severity = NORMAL, + unsigned int defStreams = aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE, + IOSystem* io = NULL); + + // ---------------------------------------------------------------------- + /** @brief Setup a custom #Logger implementation. + * + * Use this if the provided #DefaultLogger class doesn't fit into + * your needs. If the provided message formatting is OK for you, + * it's much easier to use #create() and to attach your own custom + * output streams to it. + * @param logger Pass NULL to setup a default NullLogger*/ + static void set (Logger *logger); + + // ---------------------------------------------------------------------- + /** @brief Getter for singleton instance + * @return Only instance. This is never null, but it could be a + * NullLogger. Use isNullLogger to check this.*/ + static Logger *get(); + + // ---------------------------------------------------------------------- + /** @brief Return whether a #NullLogger is currently active + * @return true if the current logger is a #NullLogger. + * Use create() or set() to setup a logger that does actually do + * something else than just rejecting all log messages. */ + static bool isNullLogger(); + + // ---------------------------------------------------------------------- + /** @brief Kills the current singleton logger and replaces it with a + * #NullLogger instance. */ + static void kill(); + + // ---------------------------------------------------------------------- + /** @copydoc Logger::attachStream */ + bool attachStream(LogStream *pStream, + unsigned int severity); + + // ---------------------------------------------------------------------- + /** @copydoc Logger::detatchStream */ + bool detatchStream(LogStream *pStream, + unsigned int severity); + + +private: + + // ---------------------------------------------------------------------- + /** @briefPrivate construction for internal use by create(). + * @param severity Logging granularity */ + explicit DefaultLogger(LogSeverity severity); + + // ---------------------------------------------------------------------- + /** @briefDestructor */ + ~DefaultLogger(); + +private: + + /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ + void OnDebug(const char* message); + + /** @brief Logs an info message */ + void OnInfo(const char* message); + + /** @brief Logs a warning message */ + void OnWarn(const char* message); + + /** @brief Logs an error message */ + void OnError(const char* message); + + // ---------------------------------------------------------------------- + /** @brief Writes a message to all streams */ + void WriteToStreams(const char* message, ErrorSeverity ErrorSev ); + + // ---------------------------------------------------------------------- + /** @brief Returns the thread id. + * @note This is an OS specific feature, if not supported, a + * zero will be returned. + */ + unsigned int GetThreadID(); + +private: + // Aliases for stream container + typedef std::vector StreamArray; + typedef std::vector::iterator StreamIt; + typedef std::vector::const_iterator ConstStreamIt; + + //! only logging instance + static Logger *m_pLogger; + static NullLogger s_pNullLogger; + + //! Attached streams + StreamArray m_StreamArray; + + bool noRepeatMsg; + char lastMsg[MAX_LOG_MESSAGE_LENGTH*2]; + size_t lastLen; +}; +// ------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! INCLUDED_AI_DEFAULTLOGGER diff --git a/Assimp/include/assimp/Defines.h b/Assimp/include/assimp/Defines.h new file mode 100644 index 0000000..15e1d83 --- /dev/null +++ b/Assimp/include/assimp/Defines.h @@ -0,0 +1,49 @@ +/* +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. + +---------------------------------------------------------------------- +*/ + +// We need those constants, workaround for any platforms where nobody defined them yet +#if (!defined SIZE_MAX) +# define SIZE_MAX (~((size_t)0)) +#endif + +#if (!defined UINT_MAX) +# define UINT_MAX (~((unsigned int)0)) +#endif + diff --git a/Assimp/include/assimp/Exporter.hpp b/Assimp/include/assimp/Exporter.hpp new file mode 100644 index 0000000..c6a6f68 --- /dev/null +++ b/Assimp/include/assimp/Exporter.hpp @@ -0,0 +1,483 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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.hpp +* @brief Defines the CPP-API for the Assimp export interface +*/ +#pragma once +#ifndef AI_EXPORT_HPP_INC +#define AI_EXPORT_HPP_INC + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include "cexport.h" +#include + +namespace Assimp { + +class ExporterPimpl; +class IOSystem; + +// ---------------------------------------------------------------------------------- +/** CPP-API: The Exporter class forms an C++ interface to the export functionality + * of the Open Asset Import Library. Note that the export interface is available + * only if Assimp has been built with ASSIMP_BUILD_NO_EXPORT not defined. + * + * The interface is modeled after the importer interface and mostly + * symmetric. The same rules for threading etc. apply. + * + * In a nutshell, there are two export interfaces: #Export, which writes the + * output file(s) either to the regular file system or to a user-supplied + * #IOSystem, and #ExportToBlob which returns a linked list of memory + * buffers (blob), each referring to one output file (in most cases + * there will be only one output file of course, but this extra complexity is + * needed since Assimp aims at supporting a wide range of file formats). + * + * #ExportToBlob is especially useful if you intend to work + * with the data in-memory. +*/ +class ASSIMP_API ExportProperties; + +class ASSIMP_API Exporter { +public: + /** Function pointer type of a Export worker function */ + typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*); + + /** Internal description of an Assimp export format option */ + struct ExportFormatEntry + { + /// Public description structure to be returned by aiGetExportFormatDescription() + aiExportFormatDesc mDescription; + + // Worker function to do the actual exporting + fpExportFunc mExportFunction; + + // Post-processing steps to be executed PRIOR to invoking mExportFunction + unsigned int mEnforcePP; + + // Constructor to fill all entries + ExportFormatEntry( const char* pId, const char* pDesc, const char* pExtension, fpExportFunc pFunction, unsigned int pEnforcePP = 0u) + { + mDescription.id = pId; + mDescription.description = pDesc; + mDescription.fileExtension = pExtension; + mExportFunction = pFunction; + mEnforcePP = pEnforcePP; + } + + ExportFormatEntry() : + mExportFunction() + , mEnforcePP() + { + mDescription.id = NULL; + mDescription.description = NULL; + mDescription.fileExtension = NULL; + } + }; + + +public: + Exporter(); + ~Exporter(); + +public: + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the exporter to use to open and + * access files. + * + * If you need #Export to use custom IO logic to access the files, + * you need to supply a custom implementation of IOSystem and + * IOFile to the exporter. + * + * #Exporter takes ownership of the object and will destroy it + * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation, which uses plain file IO. + * + * @param pIOHandler The IO handler to be used in all file accesses + * of the Importer. */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Retrieves the IO handler that is currently set. + * You can use #IsDefaultIOHandler() to check whether the returned + * interface is the default IO handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom IO handler via #SetIOHandler(). + * @return A valid IOSystem interface, never NULL. */ + IOSystem* GetIOHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default IO handler is active + * A default handler is active as long the application doesn't + * supply its own custom IO handler via #SetIOHandler(). + * @return true by default */ + bool IsDefaultIOHandler() const; + + // ------------------------------------------------------------------- + /** Exports the given scene to a chosen file format. Returns the exported + * data as a binary blob which you can write into a file or something. + * When you're done with the data, simply let the #Exporter instance go + * out of scope to have it released automatically. + * @param pScene The scene to export. Stays in possession of the caller, + * is not changed by the function. + * @param pFormatId ID string to specify to which format you want to + * export to. Use + * #GetExportFormatCount / #GetExportFormatDescription to learn which + * export formats are available. + * @param pPreprocessing See the documentation for #Export + * @return the exported data or NULL in case of error. + * @note If the Exporter instance did already hold a blob from + * a previous call to #ExportToBlob, it will be disposed. + * Any IO handlers set via #SetIOHandler are ignored here. + * @note Use aiCopyScene() to get a modifiable copy of a previously + * imported scene. */ + const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* = NULL); + const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); + + // ------------------------------------------------------------------- + /** Convenience function to export directly to a file. Use + * #SetIOSystem to supply a custom IOSystem to gain fine-grained control + * about the output data flow of the export process. + * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL. + * @param pPath Full target file name. Target must be accessible. + * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated + * flags, but in reality only a subset of them makes sense here. Specifying + * 'preprocessing' flags is useful if the input scene does not conform to + * Assimp's default conventions as specified in the @link data Data Structures Page @endlink. + * In short, this means the geometry data should use a right-handed coordinate systems, face + * winding should be counter-clockwise and the UV coordinate origin is assumed to be in + * the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder flags are used in the import side to allow users + * to have those defaults automatically adapted to their conventions. Specifying those flags + * for exporting has the opposite effect, respectively. Some other of the + * #aiPostProcessSteps enumerated values may be useful as well, but you'll need + * to try out what their effect on the exported file is. Many formats impose + * their own restrictions on the structure of the geometry stored therein, + * so some preprocessing may have little or no effect at all, or may be + * redundant as exporters would apply them anyhow. A good example + * is triangulation - whilst you can enforce it by specifying + * the #aiProcess_Triangulate flag, most export formats support only + * triangulate data so they would run the step even if it wasn't requested. + * + * If assimp detects that the input scene was directly taken from the importer side of + * the library (i.e. not copied using aiCopyScene and potentially modified afterwards), + * any post-processing steps already applied to the scene will not be applied again, unless + * they show non-idempotent behavior (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder). + * @return AI_SUCCESS if everything was fine. + * @note Use aiCopyScene() to get a modifiable copy of a previously + * imported scene.*/ + aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); + aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in #Export + * or #ExportToBlob + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #Export, #ExportToBlob, #FreeBlob */ + const char* GetErrorString() const; + + // ------------------------------------------------------------------- + /** Return the blob obtained from the last call to #ExportToBlob */ + const aiExportDataBlob* GetBlob() const; + + // ------------------------------------------------------------------- + /** Orphan the blob from the last call to #ExportToBlob. This means + * the caller takes ownership and is thus responsible for calling + * the C API function #aiReleaseExportBlob to release it. */ + const aiExportDataBlob* GetOrphanedBlob() const; + + // ------------------------------------------------------------------- + /** Frees the current blob. + * + * The function does nothing if no blob has previously been + * previously produced via #ExportToBlob. #FreeBlob is called + * automatically by the destructor. The only reason to call + * it manually would be to reclaim as much storage as possible + * without giving up the #Exporter instance yet. */ + void FreeBlob( ); + + // ------------------------------------------------------------------- + /** Returns the number of export file formats available in the current + * Assimp build. Use #Exporter::GetExportFormatDescription to + * retrieve infos of a specific export format. + * + * This includes built-in exporters as well as exporters registered + * using #RegisterExporter. + **/ + size_t GetExportFormatCount() const; + + // ------------------------------------------------------------------- + /** Returns a description of the nth export file format. Use # + * #Exporter::GetExportFormatCount to learn how many export + * formats are supported. + * + * The returned pointer is of static storage duration if the + * pIndex pertains to a built-in exporter (i.e. one not registered + * via #RegistrerExporter). It is restricted to the life-time of the + * #Exporter instance otherwise. + * + * @param pIndex Index of the export format to retrieve information + * for. Valid range is 0 to #Exporter::GetExportFormatCount + * @return A description of that specific export format. + * NULL if pIndex is out of range. */ + const aiExportFormatDesc* GetExportFormatDescription( size_t pIndex ) const; + + // ------------------------------------------------------------------- + /** Register a custom exporter. Custom export formats are limited to + * to the current #Exporter instance and do not affect the + * library globally. The indexes under which the format's + * export format description can be queried are assigned + * monotonously. + * @param desc Exporter description. + * @return aiReturn_SUCCESS if the export format was successfully + * registered. A common cause that would prevent an exporter + * from being registered is that its format id is already + * occupied by another format. */ + aiReturn RegisterExporter(const ExportFormatEntry& desc); + + // ------------------------------------------------------------------- + /** Remove an export format previously registered with #RegisterExporter + * from the #Exporter instance (this can also be used to drop + * built-in exporters because those are implicitly registered + * using #RegisterExporter). + * @param id Format id to be unregistered, this refers to the + * 'id' field of #aiExportFormatDesc. + * @note Calling this method on a format description not yet registered + * has no effect.*/ + void UnregisterExporter(const char* id); + +protected: + // Just because we don't want you to know how we're hacking around. + ExporterPimpl* pimpl; +}; + +class ASSIMP_API ExportProperties { +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 IntPropertyMap; + typedef std::map FloatPropertyMap; + typedef std::map StringPropertyMap; + typedef std::map MatrixPropertyMap; + +public: + /** Standard constructor + * @see ExportProperties() + */ + ExportProperties(); + + // ------------------------------------------------------------------- + /** Copy constructor. + * + * This copies the configuration properties of another ExportProperties. + * @see ExportProperties(const ExportProperties& other) + */ + ExportProperties(const ExportProperties& other); + + // ------------------------------------------------------------------- + /** Set an integer configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX and are simple strings). + * @param iValue New value of the property + * @return true if the property was set before. The new value replaces + * the previous value in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + bool SetPropertyInteger(const char* szName, int iValue); + + // ------------------------------------------------------------------- + /** Set a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see SetPropertyInteger() + */ + bool SetPropertyBool(const char* szName, bool value) { + return SetPropertyInteger(szName,value); + } + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyFloat(const char* szName, ai_real fValue); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyString(const char* szName, const std::string& sValue); + + // ------------------------------------------------------------------- + /** Set a matrix configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue); + + // ------------------------------------------------------------------- + /** Get a configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. + * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see GetPropertyInteger() + */ + bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const { + return GetPropertyInteger(szName,bErrorReturn)!=0; + } + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + ai_real GetPropertyFloat(const char* szName, + ai_real fErrorReturn = 10e10f) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; + + // ------------------------------------------------------------------- + /** Get a matrix configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const aiMatrix4x4 GetPropertyMatrix(const char* szName, + const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const; + + // ------------------------------------------------------------------- + /** Determine a integer configuration property has been set. + * @see HasPropertyInteger() + */ + bool HasPropertyInteger(const char* szName) const; + + /** Determine a boolean configuration property has been set. + * @see HasPropertyBool() + */ + bool HasPropertyBool(const char* szName) const; + + /** Determine a boolean configuration property has been set. + * @see HasPropertyFloat() + */ + bool HasPropertyFloat(const char* szName) const; + + /** Determine a String configuration property has been set. + * @see HasPropertyString() + */ + bool HasPropertyString(const char* szName) const; + + /** Determine a Matrix configuration property has been set. + * @see HasPropertyMatrix() + */ + bool HasPropertyMatrix(const char* szName) const; + +protected: + + /** 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; +}; + +// ---------------------------------------------------------------------------------- +inline +const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const std::string& pFormatId, + unsigned int pPreprocessing, const ExportProperties* pProperties) +{ + return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing, pProperties); +} + +// ---------------------------------------------------------------------------------- +inline +aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId, + const std::string& pPath, unsigned int pPreprocessing, + const ExportProperties* pProperties) +{ + return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing, pProperties); +} + +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_EXPORT +#endif // AI_EXPORT_HPP_INC diff --git a/Assimp/include/assimp/IOStream.hpp b/Assimp/include/assimp/IOStream.hpp new file mode 100644 index 0000000..ce5907a --- /dev/null +++ b/Assimp/include/assimp/IOStream.hpp @@ -0,0 +1,140 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 IOStream.hpp + * @brief File I/O wrappers for C++. + */ + +#pragma once +#ifndef AI_IOSTREAM_H_INC +#define AI_IOSTREAM_H_INC + +#include "types.h" + +#ifndef __cplusplus +# error This header requires C++ to be used. aiFileIO.h is the \ + corresponding C interface. +#endif + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** @brief CPP-API: Class to handle file I/O for C++ + * + * Derive an own implementation from this interface to provide custom IO handling + * to the Importer. If you implement this interface, be sure to also provide an + * implementation for IOSystem that creates instances of your custom IO class. +*/ +class ASSIMP_API IOStream +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** Constructor protected, use IOSystem::Open() to create an instance. */ + IOStream(void); + +public: + // ------------------------------------------------------------------- + /** @brief Destructor. Deleting the object closes the underlying file, + * alternatively you may use IOSystem::Close() to release the file. + */ + virtual ~IOStream(); + + // ------------------------------------------------------------------- + /** @brief Read from the file + * + * See fread() for more details + * This fails for write-only files */ + virtual size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + // ------------------------------------------------------------------- + /** @brief Write to the file + * + * See fwrite() for more details + * This fails for read-only files */ + virtual size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + // ------------------------------------------------------------------- + /** @brief Set the read/write cursor of the file + * + * Note that the offset is _negative_ for aiOrigin_END. + * See fseek() for more details */ + virtual aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) = 0; + + // ------------------------------------------------------------------- + /** @brief Get the current position of the read/write cursor + * + * See ftell() for more details */ + virtual size_t Tell() const = 0; + + // ------------------------------------------------------------------- + /** @brief Returns filesize + * Returns the filesize. */ + virtual size_t FileSize() const = 0; + + // ------------------------------------------------------------------- + /** @brief Flush the contents of the file buffer (for writers) + * See fflush() for more details. + */ + virtual void Flush() = 0; +}; //! class IOStream + +// ---------------------------------------------------------------------------------- +inline IOStream::IOStream() +{ + // empty +} + +// ---------------------------------------------------------------------------------- +inline IOStream::~IOStream() +{ + // empty +} +// ---------------------------------------------------------------------------------- +} //!namespace Assimp + +#endif //!!AI_IOSTREAM_H_INC diff --git a/Assimp/include/assimp/IOSystem.hpp b/Assimp/include/assimp/IOSystem.hpp new file mode 100644 index 0000000..f4fbb60 --- /dev/null +++ b/Assimp/include/assimp/IOSystem.hpp @@ -0,0 +1,359 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 IOSystem.hpp + * @brief File system wrapper for C++. Inherit this class to supply + * custom file handling logic to the Import library. +*/ + +#pragma once +#ifndef AI_IOSYSTEM_H_INC +#define AI_IOSYSTEM_H_INC + +#ifndef __cplusplus +# error This header requires C++ to be used. aiFileIO.h is the \ + corresponding C interface. +#endif + +#include "types.h" + +#ifdef _WIN32 +# include +# include +# include +#else +# include +# include +# include +#endif // _WIN32 + +#include + +namespace Assimp { + + class IOStream; + +// --------------------------------------------------------------------------- +/** @brief CPP-API: Interface to the file system. + * + * Derive an own implementation from this interface to supply custom file handling + * to the importer library. If you implement this interface, you also want to + * supply a custom implementation for IOStream. + * + * @see Importer::SetIOHandler() + */ +class ASSIMP_API IOSystem +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +public: + + // ------------------------------------------------------------------- + /** @brief Default constructor. + * + * Create an instance of your derived class and assign it to an + * #Assimp::Importer instance by calling Importer::SetIOHandler(). + */ + IOSystem(); + + // ------------------------------------------------------------------- + /** @brief Virtual destructor. + * + * It is safe to be called from within DLL Assimp, we're constructed + * on Assimp's heap. + */ + virtual ~IOSystem(); + + +public: + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see Exists(const char*) + */ + AI_FORCE_INLINE bool Exists( const std::string& pFile) const; + + // ------------------------------------------------------------------- + /** @brief Tests for the existence of a file at the given path. + * + * @param pFile Path to the file + * @return true if there is a file with this path, else false. + */ + virtual bool Exists( const char* pFile) const = 0; + + // ------------------------------------------------------------------- + /** @brief Returns the system specific directory separator + * @return System specific directory separator + */ + virtual char getOsSeparator() const = 0; + + // ------------------------------------------------------------------- + /** @brief Open a new file with a given path. + * + * When the access to the file is finished, call Close() to release + * all associated resources (or the virtual dtor of the IOStream). + * + * @param pFile Path to the file + * @param pMode Desired file I/O mode. Required are: "wb", "w", "wt", + * "rb", "r", "rt". + * + * @return New IOStream interface allowing the lib to access + * the underlying file. + * @note When implementing this class to provide custom IO handling, + * you probably have to supply an own implementation of IOStream as well. + */ + virtual IOStream* Open(const char* pFile, + const char* pMode = "rb") = 0; + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see Open(const char*, const char*) + */ + inline IOStream* Open(const std::string& pFile, + const std::string& pMode = std::string("rb")); + + // ------------------------------------------------------------------- + /** @brief Closes the given file and releases all resources + * associated with it. + * @param pFile The file instance previously created by Open(). + */ + virtual void Close( IOStream* pFile) = 0; + + // ------------------------------------------------------------------- + /** @brief Compares two paths and check whether the point to + * identical files. + * + * The dummy implementation of this virtual member performs a + * case-insensitive comparison of the given strings. The default IO + * system implementation uses OS mechanisms to convert relative into + * absolute paths, so the result can be trusted. + * @param one First file + * @param second Second file + * @return true if the paths point to the same file. The file needn't + * be existing, however. + */ + virtual bool ComparePaths (const char* one, + const char* second) const; + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see ComparePaths(const char*, const char*) + */ + inline bool ComparePaths (const std::string& one, + const std::string& second) const; + + // ------------------------------------------------------------------- + /** @brief Pushes a new directory onto the directory stack. + * @param path Path to push onto the stack. + * @return True, when push was successful, false if path is empty. + */ + virtual bool PushDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Returns the top directory from the stack. + * @return The directory on the top of the stack. + * Returns empty when no directory was pushed to the stack. + */ + virtual const std::string &CurrentDirectory() const; + + // ------------------------------------------------------------------- + /** @brief Returns the number of directories stored on the stack. + * @return The number of directories of the stack. + */ + virtual size_t StackSize() const; + + // ------------------------------------------------------------------- + /** @brief Pops the top directory from the stack. + * @return True, when a directory was on the stack. False if no + * directory was on the stack. + */ + virtual bool PopDirectory(); + + // ------------------------------------------------------------------- + /** @brief CReates an new directory at the given path. + * @param path [in] The path to create. + * @return True, when a directory was created. False if the directory + * cannot be created. + */ + virtual bool CreateDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Will change the current directory to the given path. + * @param path [in] The path to change to. + * @return True, when the directory has changed successfully. + */ + virtual bool ChangeDirectory( const std::string &path ); + + virtual bool DeleteFile( const std::string &file ); + +private: + std::vector m_pathStack; +}; + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOSystem::IOSystem() +: m_pathStack() { + // empty +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOSystem::~IOSystem() { + // empty +} + +// ---------------------------------------------------------------------------- +// For compatibility, the interface of some functions taking a std::string was +// changed to const char* to avoid crashes between binary incompatible STL +// versions. This code her is inlined, so it shouldn't cause any problems. +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return Open(pFile.c_str(),pMode.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::Exists( const std::string& pFile) const { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return Exists(pFile.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return ComparePaths(one.c_str(),second.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::PushDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + + m_pathStack.push_back( path ); + + return true; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +const std::string &IOSystem::CurrentDirectory() const { + if ( m_pathStack.empty() ) { + static const std::string Dummy(""); + return Dummy; + } + return m_pathStack[ m_pathStack.size()-1 ]; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +size_t IOSystem::StackSize() const { + return m_pathStack.size(); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::PopDirectory() { + if ( m_pathStack.empty() ) { + return false; + } + + m_pathStack.pop_back(); + + return true; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::CreateDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_mkdir( path.c_str() ); +#else + return 0 != ::mkdir( path.c_str(), 0777 ); +#endif // _WIN32 +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ChangeDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_chdir( path.c_str() ); +#else + return 0 != ::chdir( path.c_str() ); +#endif // _WIN32 +} + + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::DeleteFile( const std::string &file ) { + if ( file.empty() ) { + return false; + } + const int retCode( ::remove( file.c_str() ) ); + return ( 0 == retCode ); +} +} //!ns Assimp + +#endif //AI_IOSYSTEM_H_INC diff --git a/Assimp/include/assimp/Importer.hpp b/Assimp/include/assimp/Importer.hpp new file mode 100644 index 0000000..f42a2de --- /dev/null +++ b/Assimp/include/assimp/Importer.hpp @@ -0,0 +1,653 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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.hpp + * @brief Defines the C++-API to the Open Asset Import Library. + */ +#pragma once +#ifndef AI_ASSIMP_HPP_INC +#define AI_ASSIMP_HPP_INC + +#ifndef __cplusplus +# error This header requires C++ to be used. Use assimp.h for plain C. +#endif // __cplusplus + +// Public ASSIMP data structures +#include + +namespace Assimp { + // ======================================================================= + // Public interface to Assimp + class Importer; + class IOStream; + class IOSystem; + class ProgressHandler; + + // ======================================================================= + // Plugin development + // + // Include the following headers for the declarations: + // BaseImporter.h + // BaseProcess.h + class BaseImporter; + class BaseProcess; + class SharedPostProcessInfo; + class BatchLoader; + + // ======================================================================= + // Holy stuff, only for members of the high council of the Jedi. + class ImporterPimpl; +} //! namespace Assimp + +#define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff + +struct aiScene; + +// importerdesc.h +struct aiImporterDesc; + +/** @namespace Assimp Assimp's CPP-API and all internal APIs */ +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** CPP-API: The Importer class forms an C++ interface to the functionality of the +* Open Asset Import Library. +* +* Create an object of this class and call ReadFile() to import a file. +* If the import succeeds, the function returns a pointer to the imported data. +* The data remains property of the object, it is intended to be accessed +* read-only. The imported data will be destroyed along with the Importer +* object. If the import fails, ReadFile() returns a NULL pointer. In this +* case you can retrieve a human-readable error description be calling +* GetErrorString(). You can call ReadFile() multiple times with a single Importer +* instance. Actually, constructing Importer objects involves quite many +* allocations and may take some time, so it's better to reuse them as often as +* possible. +* +* If you need the Importer to do custom file handling to access the files, +* implement IOSystem and IOStream and supply an instance of your custom +* IOSystem implementation by calling SetIOHandler() before calling ReadFile(). +* If you do not assign a custion IO handler, a default handler using the +* standard C++ IO logic will be used. +* +* @note One Importer instance is not thread-safe. If you use multiple +* threads for loading, each thread should maintain its own Importer instance. +*/ +class ASSIMP_API Importer { +public: + /** + * @brief The upper limit for hints. + */ + static const unsigned int MaxLenHint = 200; + +public: + + // ------------------------------------------------------------------- + /** Constructor. Creates an empty importer object. + * + * Call ReadFile() to start the import process. The configuration + * property table is initially empty. + */ + Importer(); + + // ------------------------------------------------------------------- + /** Copy constructor. + * + * This copies the configuration properties of another Importer. + * If this Importer owns a scene it won't be copied. + * Call ReadFile() to start the import process. + */ + Importer(const Importer& other); + + // ------------------------------------------------------------------- + /** Destructor. The object kept ownership of the imported data, + * which now will be destroyed along with the object. + */ + ~Importer(); + + + // ------------------------------------------------------------------- + /** Registers a new loader. + * + * @param pImp Importer to be added. The Importer instance takes + * ownership of the pointer, so it will be automatically deleted + * with the Importer instance. + * @return AI_SUCCESS if the loader has been added. The registration + * fails if there is already a loader for a specific file extension. + */ + aiReturn RegisterLoader(BaseImporter* pImp); + + // ------------------------------------------------------------------- + /** Unregisters a loader. + * + * @param pImp Importer to be unregistered. + * @return AI_SUCCESS if the loader has been removed. The function + * fails if the loader is currently in use (this could happen + * if the #Importer instance is used by more than one thread) or + * if it has not yet been registered. + */ + aiReturn UnregisterLoader(BaseImporter* pImp); + + // ------------------------------------------------------------------- + /** Registers a new post-process step. + * + * At the moment, there's a small limitation: new post processing + * steps are added to end of the list, or in other words, executed + * last, after all built-in steps. + * @param pImp Post-process step to be added. The Importer instance + * takes ownership of the pointer, so it will be automatically + * deleted with the Importer instance. + * @return AI_SUCCESS if the step has been added correctly. + */ + aiReturn RegisterPPStep(BaseProcess* pImp); + + // ------------------------------------------------------------------- + /** Unregisters a post-process step. + * + * @param pImp Step to be unregistered. + * @return AI_SUCCESS if the step has been removed. The function + * fails if the step is currently in use (this could happen + * if the #Importer instance is used by more than one thread) or + * if it has not yet been registered. + */ + aiReturn UnregisterPPStep(BaseProcess* pImp); + + // ------------------------------------------------------------------- + /** Set an integer configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX and are simple strings). + * @param iValue New value of the property + * @return true if the property was set before. The new value replaces + * the previous value in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + bool SetPropertyInteger(const char* szName, int iValue); + + // ------------------------------------------------------------------- + /** Set a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see SetPropertyInteger() + */ + bool SetPropertyBool(const char* szName, bool value) { + return SetPropertyInteger(szName,value); + } + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyFloat(const char* szName, ai_real fValue); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyString(const char* szName, const std::string& sValue); + + // ------------------------------------------------------------------- + /** Set a matrix configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue); + + // ------------------------------------------------------------------- + /** Get a configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. + * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see GetPropertyInteger() + */ + bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const { + return GetPropertyInteger(szName,bErrorReturn)!=0; + } + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + ai_real GetPropertyFloat(const char* szName, + ai_real fErrorReturn = 10e10) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; + + // ------------------------------------------------------------------- + /** Get a matrix configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const aiMatrix4x4 GetPropertyMatrix(const char* szName, + const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const; + + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the importer to use to open and + * access files. If you need the importer to use custom IO logic to + * access the files, you need to provide a custom implementation of + * IOSystem and IOFile to the importer. Then create an instance of + * your custom IOSystem implementation and supply it by this function. + * + * The Importer takes ownership of the object and will destroy it + * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation. + * + * @param pIOHandler The IO handler to be used in all file accesses + * of the Importer. + */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Retrieves the IO handler that is currently set. + * You can use #IsDefaultIOHandler() to check whether the returned + * interface is the default IO handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom IO handler via #SetIOHandler(). + * @return A valid IOSystem interface, never NULL. + */ + IOSystem* GetIOHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default IO handler is active + * A default handler is active as long the application doesn't + * supply its own custom IO handler via #SetIOHandler(). + * @return true by default + */ + bool IsDefaultIOHandler() const; + + // ------------------------------------------------------------------- + /** Supplies a custom progress handler to the importer. This + * interface exposes a #Update() callback, which is called + * more or less periodically (please don't sue us if it + * isn't as periodically as you'd like it to have ...). + * This can be used to implement progress bars and loading + * timeouts. + * @param pHandler Progress callback interface. Pass NULL to + * disable progress reporting. + * @note Progress handlers can be used to abort the loading + * at almost any time.*/ + void SetProgressHandler ( ProgressHandler* pHandler ); + + // ------------------------------------------------------------------- + /** Retrieves the progress handler that is currently set. + * You can use #IsDefaultProgressHandler() to check whether the returned + * interface is the default handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom handler via #SetProgressHandler(). + * @return A valid ProgressHandler interface, never NULL. + */ + ProgressHandler* GetProgressHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default progress handler is active + * A default handler is active as long the application doesn't + * supply its own custom progress handler via #SetProgressHandler(). + * @return true by default + */ + bool IsDefaultProgressHandler() const; + + // ------------------------------------------------------------------- + /** @brief Check whether a given set of post-processing flags + * is supported. + * + * Some flags are mutually exclusive, others are probably + * not available because your excluded them from your + * Assimp builds. Calling this function is recommended if + * you're unsure. + * + * @param pFlags Bitwise combination of the aiPostProcess flags. + * @return true if this flag combination is fine. + */ + bool ValidateFlags(unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Reads the given file and returns its contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import fails, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). The previous scene will be deleted during this call. + * @param pFile Path and filename to the file to be imported. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). + * @return A pointer to the imported data, NULL if the import failed. + * The pointer to the scene remains in possession of the Importer + * instance. Use GetOrphanedScene() to take ownership of it. + * + * @note Assimp is able to determine the file format of a file + * automatically. + */ + const aiScene* ReadFile( + const char* pFile, + unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Reads the given file from a memory buffer and returns its + * contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import fails, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). The previous scene will be deleted during this call. + * Calling this method doesn't affect the active IOSystem. + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non + * empty string, the library looks for a loader to support + * the file extension specified by pHint and passes the file to + * the first matching loader. If this loader is unable to completely + * the request, the library continues and tries to determine the + * file format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @return A pointer to the imported data, NULL if the import failed. + * The pointer to the scene remains in possession of the Importer + * instance. Use GetOrphanedScene() to take ownership of it. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular ReadFile() API. + */ + const aiScene* ReadFileFromMemory( + const void* pBuffer, + size_t pLength, + unsigned int pFlags, + const char* pHint = ""); + + // ------------------------------------------------------------------- + /** Apply post-processing to an already-imported scene. + * + * This is strictly equivalent to calling #ReadFile() with the same + * flags. However, you can use this separate function to inspect + * the imported scene first to fine-tune your post-processing setup. + * @param pFlags Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @return A pointer to the post-processed data. This is still the + * same as the pointer returned by #ReadFile(). However, if + * post-processing fails, the scene could now be NULL. + * That's quite a rare case, post processing steps are not really + * designed to 'fail'. To be exact, the #aiProcess_ValidateDS + * flag is currently the only post processing step which can actually + * cause the scene to be reset to NULL. + * + * @note The method does nothing if no scene is currently bound + * to the #Importer instance. */ + const aiScene* ApplyPostProcessing(unsigned int pFlags); + + const aiScene* ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ); + + // ------------------------------------------------------------------- + /** @brief Reads the given file and returns its contents if successful. + * + * This function is provided for backward compatibility. + * See the const char* version for detailed docs. + * @see ReadFile(const char*, pFlags) */ + const aiScene* ReadFile( + const std::string& pFile, + unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Frees the current scene. + * + * The function does nothing if no scene has previously been + * read via ReadFile(). FreeScene() is called automatically by the + * destructor and ReadFile() itself. */ + void FreeScene( ); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in ReadFile(). + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #ReadFile(), #FreeScene(). */ + const char* GetErrorString() const; + + // ------------------------------------------------------------------- + /** Returns the scene loaded by the last successful call to ReadFile() + * + * @return Current scene or NULL if there is currently no scene loaded */ + const aiScene* GetScene() const; + + // ------------------------------------------------------------------- + /** Returns the scene loaded by the last successful call to ReadFile() + * and releases the scene from the ownership of the Importer + * instance. The application is now responsible for deleting the + * scene. Any further calls to GetScene() or GetOrphanedScene() + * will return NULL - until a new scene has been loaded via ReadFile(). + * + * @return Current scene or NULL if there is currently no scene loaded + * @note Use this method with maximal caution, and only if you have to. + * By design, aiScene's are exclusively maintained, allocated and + * deallocated by Assimp and no one else. The reasoning behind this + * is the golden rule that deallocations should always be done + * by the module that did the original allocation because heaps + * are not necessarily shared. GetOrphanedScene() enforces you + * to delete the returned scene by yourself, but this will only + * be fine if and only if you're using the same heap as assimp. + * On Windows, it's typically fine provided everything is linked + * against the multithreaded-dll version of the runtime library. + * It will work as well for static linkage with Assimp.*/ + aiScene* GetOrphanedScene(); + + // ------------------------------------------------------------------- + /** Returns whether a given file extension is supported by ASSIMP. + * + * @param szExtension Extension to be checked. + * Must include a trailing dot '.'. Example: ".3ds", ".md3". + * Cases-insensitive. + * @return true if the extension is supported, false otherwise */ + bool IsExtensionSupported(const char* szExtension) const; + + // ------------------------------------------------------------------- + /** @brief Returns whether a given file extension is supported by ASSIMP. + * + * This function is provided for backward compatibility. + * See the const char* version for detailed and up-to-date docs. + * @see IsExtensionSupported(const char*) */ + inline bool IsExtensionSupported(const std::string& szExtension) const; + + // ------------------------------------------------------------------- + /** Get a full list of all file extensions supported by ASSIMP. + * + * If a file extension is contained in the list this does of course not + * mean that ASSIMP is able to load all files with this extension --- + * it simply means there is an importer loaded which claims to handle + * files with this file extension. + * @param szOut String to receive the extension list. + * Format of the list: "*.3ds;*.obj;*.dae". This is useful for + * use with the WinAPI call GetOpenFileName(Ex). */ + void GetExtensionList(aiString& szOut) const; + + // ------------------------------------------------------------------- + /** @brief Get a full list of all file extensions supported by ASSIMP. + * + * This function is provided for backward compatibility. + * See the aiString version for detailed and up-to-date docs. + * @see GetExtensionList(aiString&)*/ + inline void GetExtensionList(std::string& szOut) const; + + // ------------------------------------------------------------------- + /** Get the number of importers currently registered with Assimp. */ + size_t GetImporterCount() const; + + // ------------------------------------------------------------------- + /** Get meta data for the importer corresponding to a specific index.. + * + * For the declaration of #aiImporterDesc, include . + * @param index Index to query, must be within [0,GetImporterCount()) + * @return Importer meta data structure, NULL if the index does not + * exist or if the importer doesn't offer meta information ( + * importers may do this at the cost of being hated by their peers).*/ + const aiImporterDesc* GetImporterInfo(size_t index) const; + + // ------------------------------------------------------------------- + /** Find the importer corresponding to a specific index. + * + * @param index Index to query, must be within [0,GetImporterCount()) + * @return Importer instance. NULL if the index does not + * exist. */ + BaseImporter* GetImporter(size_t index) const; + + // ------------------------------------------------------------------- + /** Find the importer corresponding to a specific file extension. + * + * This is quite similar to #IsExtensionSupported except a + * BaseImporter instance is returned. + * @param szExtension Extension to check for. The following formats + * are recognized (BAH being the file extension): "BAH" (comparison + * is case-insensitive), ".bah", "*.bah" (wild card and dot + * characters at the beginning of the extension are skipped). + * @return NULL if no importer is found*/ + BaseImporter* GetImporter (const char* szExtension) const; + + // ------------------------------------------------------------------- + /** Find the importer index corresponding to a specific file extension. + * + * @param szExtension Extension to check for. The following formats + * are recognized (BAH being the file extension): "BAH" (comparison + * is case-insensitive), ".bah", "*.bah" (wild card and dot + * characters at the beginning of the extension are skipped). + * @return (size_t)-1 if no importer is found */ + size_t GetImporterIndex (const char* szExtension) const; + + // ------------------------------------------------------------------- + /** Returns the storage allocated by ASSIMP to hold the scene data + * in memory. + * + * This refers to the currently loaded file, see #ReadFile(). + * @param in Data structure to be filled. + * @note The returned memory statistics refer to the actual + * size of the use data of the aiScene. Heap-related overhead + * is (naturally) not included.*/ + void GetMemoryRequirements(aiMemoryInfo& in) const; + + // ------------------------------------------------------------------- + /** Enables "extra verbose" mode. + * + * 'Extra verbose' means the data structure is validated after *every* + * single post processing step to make sure everyone modifies the data + * structure in a well-defined manner. This is a debug feature and not + * intended for use in production environments. */ + void SetExtraVerbose(bool bDo); + + // ------------------------------------------------------------------- + /** Private, do not use. */ + ImporterPimpl* Pimpl() { return pimpl; } + const ImporterPimpl* Pimpl() const { return pimpl; } + +protected: + + // Just because we don't want you to know how we're hacking around. + ImporterPimpl* pimpl; +}; //! class Importer + + +// ---------------------------------------------------------------------------- +// For compatibility, the interface of some functions taking a std::string was +// changed to const char* to avoid crashes between binary incompatible STL +// versions. This code her is inlined, so it shouldn't cause any problems. +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE const aiScene* Importer::ReadFile( const std::string& pFile,unsigned int pFlags){ + return ReadFile(pFile.c_str(),pFlags); +} +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE void Importer::GetExtensionList(std::string& szOut) const { + aiString s; + GetExtensionList(s); + szOut = s.data; +} +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE bool Importer::IsExtensionSupported(const std::string& szExtension) const { + return IsExtensionSupported(szExtension.c_str()); +} + +} // !namespace Assimp + +#endif // AI_ASSIMP_HPP_INC diff --git a/Assimp/include/assimp/LogStream.hpp b/Assimp/include/assimp/LogStream.hpp new file mode 100644 index 0000000..1052f1f --- /dev/null +++ b/Assimp/include/assimp/LogStream.hpp @@ -0,0 +1,110 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 LogStream.hpp + * @brief Abstract base class 'LogStream', representing an output log stream. + */ +#ifndef INCLUDED_AI_LOGSTREAM_H +#define INCLUDED_AI_LOGSTREAM_H + +#include "types.h" + +namespace Assimp { + +class IOSystem; + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Abstract interface for log stream implementations. + * + * Several default implementations are provided, see #aiDefaultLogStream for more + * details. Writing your own implementation of LogStream is just necessary if these + * are not enough for your purpose. */ +class ASSIMP_API LogStream +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** @brief Default constructor */ + LogStream(); + +public: + /** @brief Virtual destructor */ + virtual ~LogStream(); + + // ------------------------------------------------------------------- + /** @brief Overwrite this for your own output methods + * + * Log messages *may* consist of multiple lines and you shouldn't + * expect a consistent formatting. If you want custom formatting + * (e.g. generate HTML), supply a custom instance of Logger to + * #DefaultLogger:set(). Usually you can *expect* that a log message + * is exactly one line and terminated with a single \n character. + * @param message Message to be written */ + virtual void write(const char* message) = 0; + + // ------------------------------------------------------------------- + /** @brief Creates a default log stream + * @param streams Type of the default stream + * @param name For aiDefaultLogStream_FILE: name of the output file + * @param io For aiDefaultLogStream_FILE: IOSystem to be used to open the output + * file. Pass NULL for the default implementation. + * @return New LogStream instance. */ + static LogStream* createDefaultStream(aiDefaultLogStream stream, + const char* name = "AssimpLog.txt", + IOSystem* io = NULL); + +}; // !class LogStream + +inline +LogStream::LogStream() { + // empty +} + +inline +LogStream::~LogStream() { + // empty +} + +// ------------------------------------------------------------------------------------ +} // Namespace Assimp + +#endif diff --git a/Assimp/include/assimp/Logger.hpp b/Assimp/include/assimp/Logger.hpp new file mode 100644 index 0000000..0875b6d --- /dev/null +++ b/Assimp/include/assimp/Logger.hpp @@ -0,0 +1,268 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 Logger.hpp + * @brief Abstract base class 'Logger', base of the logging system. + */ +#ifndef INCLUDED_AI_LOGGER_H +#define INCLUDED_AI_LOGGER_H + +#include "types.h" + +namespace Assimp { + +class LogStream; + +// Maximum length of a log message. Longer messages are rejected. +#define MAX_LOG_MESSAGE_LENGTH 1024u + +// ---------------------------------------------------------------------------------- +/** @brief CPP-API: Abstract interface for logger implementations. + * Assimp provides a default implementation and uses it for almost all + * logging stuff ('DefaultLogger'). This class defines just basic logging + * behaviour and is not of interest for you. Instead, take a look at #DefaultLogger. */ +class ASSIMP_API Logger +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +public: + + // ---------------------------------------------------------------------- + /** @enum LogSeverity + * @brief Log severity to describe the granularity of logging. + */ + enum LogSeverity + { + NORMAL, //!< Normal granularity of logging + VERBOSE //!< Debug infos will be logged, too + }; + + // ---------------------------------------------------------------------- + /** @enum ErrorSeverity + * @brief Description for severity of a log message. + * + * Every LogStream has a bitwise combination of these flags. + * A LogStream doesn't receive any messages of a specific type + * if it doesn't specify the corresponding ErrorSeverity flag. + */ + enum ErrorSeverity + { + Debugging = 1, //!< Debug log message + Info = 2, //!< Info log message + Warn = 4, //!< Warn log message + Err = 8 //!< Error log message + }; + +public: + + /** @brief Virtual destructor */ + virtual ~Logger(); + + // ---------------------------------------------------------------------- + /** @brief Writes a debug message + * @param message Debug message*/ + void debug(const char* message); + inline void debug(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes a info message + * @param message Info message*/ + void info(const char* message); + inline void info(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes a warning message + * @param message Warn message*/ + void warn(const char* message); + inline void warn(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes an error message + * @param message Error message*/ + void error(const char* message); + inline void error(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Set a new log severity. + * @param log_severity New severity for logging*/ + void setLogSeverity(LogSeverity log_severity); + + // ---------------------------------------------------------------------- + /** @brief Get the current log severity*/ + LogSeverity getLogSeverity() const; + + // ---------------------------------------------------------------------- + /** @brief Attach a new log-stream + * + * The logger takes ownership of the stream and is responsible + * for its destruction (which is done using ::delete when the logger + * itself is destroyed). Call detachStream to detach a stream and to + * gain ownership of it again. + * @param pStream Log-stream to attach + * @param severity Message filter, specified which types of log + * messages are dispatched to the stream. Provide a bitwise + * combination of the ErrorSeverity flags. + * @return true if the stream has been attached, false otherwise.*/ + virtual bool attachStream(LogStream *pStream, + unsigned int severity = Debugging | Err | Warn | Info) = 0; + + // ---------------------------------------------------------------------- + /** @brief Detach a still attached stream from the logger (or + * modify the filter flags bits) + * @param pStream Log-stream instance for detaching + * @param severity Provide a bitwise combination of the ErrorSeverity + * flags. This value is &~ed with the current flags of the stream, + * if the result is 0 the stream is detached from the Logger and + * the caller retakes the possession of the stream. + * @return true if the stream has been detached, false otherwise.*/ + virtual bool detatchStream(LogStream *pStream, + unsigned int severity = Debugging | Err | Warn | Info) = 0; + +protected: + + /** Default constructor */ + Logger(); + + /** Construction with a given log severity */ + explicit Logger(LogSeverity severity); + + // ---------------------------------------------------------------------- + /** @brief Called as a request to write a specific debug message + * @param message Debug message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (excluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnDebug(const char* message)= 0; + + // ---------------------------------------------------------------------- + /** @brief Called as a request to write a specific info message + * @param message Info message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (ecxluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnInfo(const char* message) = 0; + + // ---------------------------------------------------------------------- + /** @brief Called as a request to write a specific warn message + * @param message Warn message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnWarn(const char* essage) = 0; + + // ---------------------------------------------------------------------- + /** @brief Called as a request to write a specific error message + * @param message Error message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnError(const char* message) = 0; + +protected: + + //! Logger severity + LogSeverity m_Severity; +}; + +// ---------------------------------------------------------------------------------- +// Default constructor +inline Logger::Logger() { + setLogSeverity(NORMAL); +} + +// ---------------------------------------------------------------------------------- +// Virtual destructor +inline Logger::~Logger() +{ +} + +// ---------------------------------------------------------------------------------- +// Construction with given logging severity +inline Logger::Logger(LogSeverity severity) { + setLogSeverity(severity); +} + +// ---------------------------------------------------------------------------------- +// Log severity setter +inline void Logger::setLogSeverity(LogSeverity log_severity){ + m_Severity = log_severity; +} + +// ---------------------------------------------------------------------------------- +// Log severity getter +inline Logger::LogSeverity Logger::getLogSeverity() const { + return m_Severity; +} + +// ---------------------------------------------------------------------------------- +inline void Logger::debug(const std::string &message) +{ + return debug(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline void Logger::error(const std::string &message) +{ + return error(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline void Logger::warn(const std::string &message) +{ + return warn(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline void Logger::info(const std::string &message) +{ + return info(message.c_str()); +} + +// ---------------------------------------------------------------------------------- + +} // Namespace Assimp + +#endif // !! INCLUDED_AI_LOGGER_H diff --git a/Assimp/include/assimp/NullLogger.hpp b/Assimp/include/assimp/NullLogger.hpp new file mode 100644 index 0000000..191db1a --- /dev/null +++ b/Assimp/include/assimp/NullLogger.hpp @@ -0,0 +1,98 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 NullLogger.hpp + * @brief Dummy logger +*/ + +#ifndef INCLUDED_AI_NULLLOGGER_H +#define INCLUDED_AI_NULLLOGGER_H + +#include "Logger.hpp" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @brief CPP-API: Empty logging implementation. + * + * Does nothing! Used by default if the application hasn't requested a + * custom logger via #DefaultLogger::set() or #DefaultLogger::create(); */ +class ASSIMP_API NullLogger + : public Logger { + +public: + + /** @brief Logs a debug message */ + void OnDebug(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs an info message */ + void OnInfo(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs a warning message */ + void OnWarn(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs an error message */ + void OnError(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Detach a still attached stream from logger */ + bool attachStream(LogStream *pStream, unsigned int severity) { + (void)pStream; (void)severity; //this avoids compiler warnings + return false; + } + + /** @brief Detach a still attached stream from logger */ + bool detatchStream(LogStream *pStream, unsigned int severity) { + (void)pStream; (void)severity; //this avoids compiler warnings + return false; + } + +private: +}; +} +#endif // !! AI_NULLLOGGER_H_INCLUDED diff --git a/Assimp/include/assimp/ProgressHandler.hpp b/Assimp/include/assimp/ProgressHandler.hpp new file mode 100644 index 0000000..2c5b2f4 --- /dev/null +++ b/Assimp/include/assimp/ProgressHandler.hpp @@ -0,0 +1,126 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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'. + */ +#pragma once +#ifndef AI_PROGRESSHANDLER_H_INC +#define AI_PROGRESSHANDLER_H_INC + +#include "types.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Abstract interface for custom progress report receivers. + * + * Each #Importer instance maintains its own #ProgressHandler. The default + * implementation provided by Assimp doesn't do anything at all. */ +class ASSIMP_API ProgressHandler +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** @brief Default constructor */ + ProgressHandler () { + } +public: + /** @brief Virtual destructor */ + virtual ~ProgressHandler () { + } + + // ------------------------------------------------------------------- + /** @brief Progress callback. + * @param percentage An estimate of the current loading progress, + * in percent. Or -1.f if such an estimate is not available. + * + * There are restriction on what you may do from within your + * implementation of this method: no exceptions may be thrown and no + * non-const #Importer methods may be called. It is + * not generally possible to predict the number of callbacks + * fired during a single import. + * + * @return Return false to abort loading at the next possible + * occasion (loaders and Assimp are generally allowed to perform + * all needed cleanup tasks prior to returning control to the + * caller). If the loading is aborted, #Importer::ReadFile() + * returns always NULL. + * */ + virtual bool Update(float percentage = -1.f) = 0; + + // ------------------------------------------------------------------- + /** @brief Progress callback for file loading steps + * @param numberOfSteps The number of total post-processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * + * @note This is currently only used at the start and the end + * of the file parsing. + * */ + virtual void UpdateFileRead(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update( f * 0.5f ); + } + + // ------------------------------------------------------------------- + /** @brief Progress callback for post-processing steps + * @param numberOfSteps The number of total post-processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * */ + virtual void UpdatePostProcess(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update( f * 0.5f + 0.5f ); + } + +}; // !class ProgressHandler +// ------------------------------------------------------------------------------------ +} // Namespace Assimp + +#endif // AI_PROGRESSHANDLER_H_INC diff --git a/Assimp/include/assimp/SceneCombiner.h b/Assimp/include/assimp/SceneCombiner.h new file mode 100644 index 0000000..aa57406 --- /dev/null +++ b/Assimp/include/assimp/SceneCombiner.h @@ -0,0 +1,400 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 Declares a helper class, "SceneCombiner" providing various + * utilities to merge scenes. + */ +#ifndef AI_SCENE_COMBINER_H_INC +#define AI_SCENE_COMBINER_H_INC + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct aiScene; +struct aiNode; +struct aiMaterial; +struct aiTexture; +struct aiCamera; +struct aiLight; +struct aiMetadata; +struct aiBone; +struct aiMesh; +struct aiAnimation; +struct aiNodeAnim; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper data structure for SceneCombiner. + * + * Describes to which node a scene must be attached to. + */ +struct AttachmentInfo +{ + AttachmentInfo() + : scene (NULL) + , attachToNode (NULL) + {} + + AttachmentInfo(aiScene* _scene, aiNode* _attachToNode) + : scene (_scene) + , attachToNode (_attachToNode) + {} + + aiScene* scene; + aiNode* attachToNode; +}; + +// --------------------------------------------------------------------------- +struct NodeAttachmentInfo +{ + NodeAttachmentInfo() + : node (NULL) + , attachToNode (NULL) + , resolved (false) + , src_idx (SIZE_MAX) + {} + + NodeAttachmentInfo(aiNode* _scene, aiNode* _attachToNode,size_t idx) + : node (_scene) + , attachToNode (_attachToNode) + , resolved (false) + , src_idx (idx) + {} + + aiNode* node; + aiNode* attachToNode; + bool resolved; + size_t src_idx; +}; + +// --------------------------------------------------------------------------- +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES + * Generate unique names for all named scene items + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES 0x1 + +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES + * Generate unique names for materials, too. + * This is not absolutely required to pass the validation. + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES 0x2 + +/** @def AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY + * Use deep copies of duplicate scenes + */ +#define AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY 0x4 + +/** @def AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS + * If attachment nodes are not found in the given master scene, + * search the other imported scenes for them in an any order. + */ +#define AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS 0x8 + +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY + * Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES. + * Unique names are generated, but only if this is absolutely + * required to avoid name conflicts. + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10 + +typedef std::pair BoneSrcIndex; + +// --------------------------------------------------------------------------- +/** @brief Helper data structure for SceneCombiner::MergeBones. + */ +struct BoneWithHash : public std::pair { + std::vector pSrcBones; +}; + +// --------------------------------------------------------------------------- +/** @brief Utility for SceneCombiner + */ +struct SceneHelper +{ + SceneHelper () + : scene (NULL) + , idlen (0) + { + id[0] = 0; + } + + explicit SceneHelper (aiScene* _scene) + : scene (_scene) + , idlen (0) + { + id[0] = 0; + } + + AI_FORCE_INLINE aiScene* operator-> () const + { + return scene; + } + + // scene we're working on + aiScene* scene; + + // prefix to be added to all identifiers in the scene ... + char id [32]; + + // and its strlen() + unsigned int idlen; + + // hash table to quickly check whether a name is contained in the scene + std::set hashes; +}; + +// --------------------------------------------------------------------------- +/** \brief Static helper class providing various utilities to merge two + * scenes. It is intended as internal utility and NOT for use by + * applications. + * + * The class is currently being used by various postprocessing steps + * and loaders (ie. LWS). + */ +class ASSIMP_API SceneCombiner { + // class cannot be instanced + SceneCombiner() { + // empty + } + + ~SceneCombiner() { + // empty + } + +public: + // ------------------------------------------------------------------- + /** Merges two or more scenes. + * + * @param dest Receives a pointer to the destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param src Non-empty list of scenes to be merged. The function + * deletes the input scenes afterwards. There may be duplicate scenes. + * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above + */ + static void MergeScenes(aiScene** dest,std::vector& src, + unsigned int flags = 0); + + // ------------------------------------------------------------------- + /** Merges two or more scenes and attaches all scenes to a specific + * position in the node graph of the master scene. + * + * @param dest Receives a pointer to the destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param master Master scene. It will be deleted afterwards. All + * other scenes will be inserted in its node graph. + * @param src Non-empty list of scenes to be merged along with their + * corresponding attachment points in the master scene. The function + * deletes the input scenes afterwards. There may be duplicate scenes. + * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above + */ + static void MergeScenes(aiScene** dest, aiScene* master, + std::vector& src, + unsigned int flags = 0); + + // ------------------------------------------------------------------- + /** Merges two or more meshes + * + * The meshes should have equal vertex formats. Only components + * that are provided by ALL meshes will be present in the output mesh. + * An exception is made for VColors - they are set to black. The + * meshes should have the same material indices, too. The output + * material index is always the material index of the first mesh. + * + * @param dest Destination mesh. Must be empty. + * @param flags Currently no parameters + * @param begin First mesh to be processed + * @param end Points to the mesh after the last mesh to be processed + */ + static void MergeMeshes(aiMesh** dest,unsigned int flags, + std::vector::const_iterator begin, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Merges two or more bones + * + * @param out Mesh to receive the output bone list + * @param flags Currently no parameters + * @param begin First mesh to be processed + * @param end Points to the mesh after the last mesh to be processed + */ + static void MergeBones(aiMesh* out,std::vector::const_iterator it, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Merges two or more materials + * + * The materials should be complementary as much as possible. In case + * of a property present in different materials, the first occurrence + * is used. + * + * @param dest Destination material. Must be empty. + * @param begin First material to be processed + * @param end Points to the material after the last material to be processed + */ + static void MergeMaterials(aiMaterial** dest, + std::vector::const_iterator begin, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Builds a list of uniquely named bones in a mesh list + * + * @param asBones Receives the output list + * @param it First mesh to be processed + * @param end Last mesh to be processed + */ + static void BuildUniqueBoneList(std::list& asBones, + std::vector::const_iterator it, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Add a name prefix to all nodes in a scene. + * + * @param Current node. This function is called recursively. + * @param prefix Prefix to be added to all nodes + * @param len STring length + */ + static void AddNodePrefixes(aiNode* node, const char* prefix, + unsigned int len); + + // ------------------------------------------------------------------- + /** Add an offset to all mesh indices in a node graph + * + * @param Current node. This function is called recursively. + * @param offset Offset to be added to all mesh indices + */ + static void OffsetNodeMeshIndices (aiNode* node, unsigned int offset); + + // ------------------------------------------------------------------- + /** Attach a list of node graphs to well-defined nodes in a master + * graph. This is a helper for MergeScenes() + * + * @param master Master scene + * @param srcList List of source scenes along with their attachment + * points. If an attachment point is NULL (or does not exist in + * the master graph), a scene is attached to the root of the master + * graph (as an additional child node) + * @duplicates List of duplicates. If elem[n] == n the scene is not + * a duplicate. Otherwise elem[n] links scene n to its first occurrence. + */ + static void AttachToGraph ( aiScene* master, + std::vector& srcList); + + static void AttachToGraph (aiNode* attach, + std::vector& srcList); + + + // ------------------------------------------------------------------- + /** Get a deep copy of a scene + * + * @param dest Receives a pointer to the destination scene + * @param src Source scene - remains unmodified. + */ + static void CopyScene(aiScene** dest,const aiScene* source,bool allocate = true); + + + // ------------------------------------------------------------------- + /** Get a flat copy of a scene + * + * Only the first hierarchy layer is copied. All pointer members of + * aiScene are shared by source and destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param dest Receives a pointer to the destination scene + * @param src Source scene - remains unmodified. + */ + static void CopySceneFlat(aiScene** dest,const aiScene* source); + + + // ------------------------------------------------------------------- + /** Get a deep copy of a mesh + * + * @param dest Receives a pointer to the destination mesh + * @param src Source mesh - remains unmodified. + */ + static void Copy (aiMesh** dest, const aiMesh* src); + + // similar to Copy(): + static void Copy (aiMaterial** dest, const aiMaterial* src); + static void Copy (aiTexture** dest, const aiTexture* src); + static void Copy (aiAnimation** dest, const aiAnimation* src); + static void Copy (aiCamera** dest, const aiCamera* src); + static void Copy (aiBone** dest, const aiBone* src); + static void Copy (aiLight** dest, const aiLight* src); + static void Copy (aiNodeAnim** dest, const aiNodeAnim* src); + static void Copy (aiMetadata** dest, const aiMetadata* src); + + // recursive, of course + static void Copy (aiNode** dest, const aiNode* src); + + +private: + + // ------------------------------------------------------------------- + // Same as AddNodePrefixes, but with an additional check + static void AddNodePrefixesChecked(aiNode* node, const char* prefix, + unsigned int len, + std::vector& input, + unsigned int cur); + + // ------------------------------------------------------------------- + // Add node identifiers to a hashing set + static void AddNodeHashes(aiNode* node, std::set& hashes); + + + // ------------------------------------------------------------------- + // Search for duplicate names + static bool FindNameMatch(const aiString& name, + std::vector& input, unsigned int cur); +}; + +} + +#endif // !! AI_SCENE_COMBINER_H_INC diff --git a/Assimp/include/assimp/ai_assert.h b/Assimp/include/assimp/ai_assert.h new file mode 100644 index 0000000..d8dbac8 --- /dev/null +++ b/Assimp/include/assimp/ai_assert.h @@ -0,0 +1,53 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 AI_ASSERT_H_INC +#define AI_ASSERT_H_INC + +#ifdef ASSIMP_BUILD_DEBUG +# include +# define ai_assert(expression) assert(expression) +#else +# define ai_assert(expression) +#endif // + +#endif // AI_ASSERT_H_INC diff --git a/Assimp/include/assimp/anim.h b/Assimp/include/assimp/anim.h new file mode 100644 index 0000000..f8774b8 --- /dev/null +++ b/Assimp/include/assimp/anim.h @@ -0,0 +1,572 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 anim.h + * @brief Defines the data structures in which the imported animations + * are returned. + */ +#pragma once +#ifndef AI_ANIM_H_INC +#define AI_ANIM_H_INC + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** A time-value pair specifying a certain 3D vector for the given time. */ +struct aiVectorKey +{ + /** The time of this key */ + double mTime; + + /** The value of this key */ + C_STRUCT aiVector3D mValue; + +#ifdef __cplusplus + + /// @brief The default constructor. + aiVectorKey() + : mTime( 0.0 ) + , mValue() { + // empty + } + + /// @brief Construction from a given time and key value. + + aiVectorKey(double time, const aiVector3D& value) + : mTime (time) + , mValue (value) + {} + + typedef aiVector3D elem_type; + + // Comparison operators. For use with std::find(); + bool operator == (const aiVectorKey& o) const { + return o.mValue == this->mValue; + } + bool operator != (const aiVectorKey& o) const { + return o.mValue != this->mValue; + } + + // Relational operators. For use with std::sort(); + bool operator < (const aiVectorKey& o) const { + return mTime < o.mTime; + } + bool operator > (const aiVectorKey& o) const { + return mTime > o.mTime; + } +#endif // __cplusplus +}; + +// --------------------------------------------------------------------------- +/** A time-value pair specifying a rotation for the given time. + * Rotations are expressed with quaternions. */ +struct aiQuatKey +{ + /** The time of this key */ + double mTime; + + /** The value of this key */ + C_STRUCT aiQuaternion mValue; + +#ifdef __cplusplus + aiQuatKey() + : mTime( 0.0 ) + , mValue() { + // empty + } + + /** Construction from a given time and key value */ + aiQuatKey(double time, const aiQuaternion& value) + : mTime (time) + , mValue (value) + {} + + typedef aiQuaternion elem_type; + + // Comparison operators. For use with std::find(); + bool operator == (const aiQuatKey& o) const { + return o.mValue == this->mValue; + } + bool operator != (const aiQuatKey& o) const { + return o.mValue != this->mValue; + } + + // Relational operators. For use with std::sort(); + bool operator < (const aiQuatKey& o) const { + return mTime < o.mTime; + } + bool operator > (const aiQuatKey& o) const { + return mTime > o.mTime; + } +#endif +}; + +// --------------------------------------------------------------------------- +/** Binds a anim mesh to a specific point in time. */ +struct aiMeshKey +{ + /** The time of this key */ + double mTime; + + /** Index into the aiMesh::mAnimMeshes array of the + * mesh corresponding to the #aiMeshAnim hosting this + * key frame. The referenced anim mesh is evaluated + * according to the rules defined in the docs for #aiAnimMesh.*/ + unsigned int mValue; + +#ifdef __cplusplus + + aiMeshKey() { + } + + /** Construction from a given time and key value */ + aiMeshKey(double time, const unsigned int value) + : mTime (time) + , mValue (value) + {} + + typedef unsigned int elem_type; + + // Comparison operators. For use with std::find(); + bool operator == (const aiMeshKey& o) const { + return o.mValue == this->mValue; + } + bool operator != (const aiMeshKey& o) const { + return o.mValue != this->mValue; + } + + // Relational operators. For use with std::sort(); + bool operator < (const aiMeshKey& o) const { + return mTime < o.mTime; + } + bool operator > (const aiMeshKey& o) const { + return mTime > o.mTime; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** Binds a morph anim mesh to a specific point in time. */ +struct aiMeshMorphKey +{ + /** The time of this key */ + double mTime; + + /** The values and weights at the time of this key */ + unsigned int *mValues; + double *mWeights; + + /** The number of values and weights */ + unsigned int mNumValuesAndWeights; +#ifdef __cplusplus + aiMeshMorphKey() + : mTime(0.0) + , mValues(NULL) + , mWeights(NULL) + , mNumValuesAndWeights(0) + { + + } + + ~aiMeshMorphKey() + { + if (mNumValuesAndWeights && mValues && mWeights) { + delete [] mValues; + delete [] mWeights; + } + } +#endif +}; + +// --------------------------------------------------------------------------- +/** Defines how an animation channel behaves outside the defined time + * range. This corresponds to aiNodeAnim::mPreState and + * aiNodeAnim::mPostState.*/ +enum aiAnimBehaviour +{ + /** The value from the default node transformation is taken*/ + aiAnimBehaviour_DEFAULT = 0x0, + + /** The nearest key value is used without interpolation */ + aiAnimBehaviour_CONSTANT = 0x1, + + /** The value of the nearest two keys is linearly + * extrapolated for the current time value.*/ + aiAnimBehaviour_LINEAR = 0x2, + + /** The animation is repeated. + * + * If the animation key go from n to m and the current + * time is t, use the value at (t-n) % (|m-n|).*/ + aiAnimBehaviour_REPEAT = 0x3, + + /** This value is not used, it is just here to force the + * the compiler to map this enum to a 32 Bit integer */ +#ifndef SWIG + _aiAnimBehaviour_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** Describes the animation of a single node. The name specifies the + * bone/node which is affected by this animation channel. The keyframes + * are given in three separate series of values, one each for position, + * rotation and scaling. The transformation matrix computed from these + * values replaces the node's original transformation matrix at a + * specific time. + * This means all keys are absolute and not relative to the bone default pose. + * The order in which the transformations are applied is + * - as usual - scaling, rotation, translation. + * + * @note All keys are returned in their correct, chronological order. + * Duplicate keys don't pass the validation step. Most likely there + * will be no negative time values, but they are not forbidden also ( so + * implementations need to cope with them! ) */ +struct aiNodeAnim { + /** The name of the node affected by this animation. The node + * must exist and it must be unique.*/ + C_STRUCT aiString mNodeName; + + /** The number of position keys */ + unsigned int mNumPositionKeys; + + /** The position keys of this animation channel. Positions are + * specified as 3D vector. The array is mNumPositionKeys in size. + * + * If there are position keys, there will also be at least one + * scaling and one rotation key.*/ + C_STRUCT aiVectorKey* mPositionKeys; + + /** The number of rotation keys */ + unsigned int mNumRotationKeys; + + /** The rotation keys of this animation channel. Rotations are + * given as quaternions, which are 4D vectors. The array is + * mNumRotationKeys in size. + * + * If there are rotation keys, there will also be at least one + * scaling and one position key. */ + C_STRUCT aiQuatKey* mRotationKeys; + + /** The number of scaling keys */ + unsigned int mNumScalingKeys; + + /** The scaling keys of this animation channel. Scalings are + * specified as 3D vector. The array is mNumScalingKeys in size. + * + * If there are scaling keys, there will also be at least one + * position and one rotation key.*/ + C_STRUCT aiVectorKey* mScalingKeys; + + /** Defines how the animation behaves before the first + * key is encountered. + * + * The default value is aiAnimBehaviour_DEFAULT (the original + * transformation matrix of the affected node is used).*/ + C_ENUM aiAnimBehaviour mPreState; + + /** Defines how the animation behaves after the last + * key was processed. + * + * The default value is aiAnimBehaviour_DEFAULT (the original + * transformation matrix of the affected node is taken).*/ + C_ENUM aiAnimBehaviour mPostState; + +#ifdef __cplusplus + aiNodeAnim() + : mNumPositionKeys( 0 ) + , mPositionKeys( NULL ) + , mNumRotationKeys( 0 ) + , mRotationKeys( NULL ) + , mNumScalingKeys( 0 ) + , mScalingKeys( NULL ) + , mPreState( aiAnimBehaviour_DEFAULT ) + , mPostState( aiAnimBehaviour_DEFAULT ) { + // empty + } + + ~aiNodeAnim() { + delete [] mPositionKeys; + delete [] mRotationKeys; + delete [] mScalingKeys; + } +#endif // __cplusplus +}; + +// --------------------------------------------------------------------------- +/** Describes vertex-based animations for a single mesh or a group of + * meshes. Meshes carry the animation data for each frame in their + * aiMesh::mAnimMeshes array. The purpose of aiMeshAnim is to + * define keyframes linking each mesh attachment to a particular + * point in time. */ +struct aiMeshAnim +{ + /** Name of the mesh to be animated. An empty string is not allowed, + * animated meshes need to be named (not necessarily uniquely, + * the name can basically serve as wild-card to select a group + * of meshes with similar animation setup)*/ + C_STRUCT aiString mName; + + /** Size of the #mKeys array. Must be 1, at least. */ + unsigned int mNumKeys; + + /** Key frames of the animation. May not be NULL. */ + C_STRUCT aiMeshKey* mKeys; + +#ifdef __cplusplus + + aiMeshAnim() + : mNumKeys() + , mKeys() + {} + + ~aiMeshAnim() + { + delete[] mKeys; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** Describes a morphing animation of a given mesh. */ +struct aiMeshMorphAnim +{ + /** Name of the mesh to be animated. An empty string is not allowed, + * animated meshes need to be named (not necessarily uniquely, + * the name can basically serve as wildcard to select a group + * of meshes with similar animation setup)*/ + C_STRUCT aiString mName; + + /** Size of the #mKeys array. Must be 1, at least. */ + unsigned int mNumKeys; + + /** Key frames of the animation. May not be NULL. */ + C_STRUCT aiMeshMorphKey* mKeys; + +#ifdef __cplusplus + + aiMeshMorphAnim() + : mNumKeys() + , mKeys() + {} + + ~aiMeshMorphAnim() + { + delete[] mKeys; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** An animation consists of key-frame data for a number of nodes. For + * each node affected by the animation a separate series of data is given.*/ +struct aiAnimation { + /** The name of the animation. If the modeling package this data was + * exported from does support only a single animation channel, this + * name is usually empty (length is zero). */ + C_STRUCT aiString mName; + + /** Duration of the animation in ticks. */ + double mDuration; + + /** Ticks per second. 0 if not specified in the imported file */ + double mTicksPerSecond; + + /** The number of bone animation channels. Each channel affects + * a single node. */ + unsigned int mNumChannels; + + /** The node animation channels. Each channel affects a single node. + * The array is mNumChannels in size. */ + C_STRUCT aiNodeAnim** mChannels; + + + /** The number of mesh animation channels. Each channel affects + * a single mesh and defines vertex-based animation. */ + unsigned int mNumMeshChannels; + + /** The mesh animation channels. Each channel affects a single mesh. + * The array is mNumMeshChannels in size. */ + C_STRUCT aiMeshAnim** mMeshChannels; + + /** The number of mesh animation channels. Each channel affects + * a single mesh and defines morphing animation. */ + unsigned int mNumMorphMeshChannels; + + /** The morph mesh animation channels. Each channel affects a single mesh. + * The array is mNumMorphMeshChannels in size. */ + C_STRUCT aiMeshMorphAnim **mMorphMeshChannels; + +#ifdef __cplusplus + aiAnimation() + : mDuration(-1.) + , mTicksPerSecond(0.) + , mNumChannels(0) + , mChannels(NULL) + , mNumMeshChannels(0) + , mMeshChannels(NULL) + , mNumMorphMeshChannels(0) + , mMorphMeshChannels(NULL) { + // empty + } + + ~aiAnimation() { + // DO NOT REMOVE THIS ADDITIONAL CHECK + if ( mNumChannels && mChannels ) { + for( unsigned int a = 0; a < mNumChannels; a++) { + delete mChannels[ a ]; + } + + delete [] mChannels; + } + if (mNumMeshChannels && mMeshChannels) { + for( unsigned int a = 0; a < mNumMeshChannels; a++) { + delete mMeshChannels[a]; + } + + delete [] mMeshChannels; + } + if (mNumMorphMeshChannels && mMorphMeshChannels) { + for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) { + delete mMorphMeshChannels[a]; + } + + delete [] mMorphMeshChannels; + } + } +#endif // __cplusplus +}; + +#ifdef __cplusplus + +} + +/// @brief Some C++ utilities for inter- and extrapolation +namespace Assimp { + +// --------------------------------------------------------------------------- +/** + * @brief CPP-API: Utility class to simplify interpolations of various data types. + * + * The type of interpolation is chosen automatically depending on the + * types of the arguments. + */ +template +struct Interpolator +{ + // ------------------------------------------------------------------ + /** @brief Get the result of the interpolation between a,b. + * + * The interpolation algorithm depends on the type of the operands. + * aiQuaternion's and aiQuatKey's SLERP, the rest does a simple + * linear interpolation. */ + void operator () (T& out,const T& a, const T& b, ai_real d) const { + out = a + (b-a)*d; + } +}; // ! Interpolator + +//! @cond Never + +template <> +struct Interpolator { + void operator () (aiQuaternion& out,const aiQuaternion& a, + const aiQuaternion& b, ai_real d) const + { + aiQuaternion::Interpolate(out,a,b,d); + } +}; // ! Interpolator + +template <> +struct Interpolator { + void operator () (unsigned int& out,unsigned int a, + unsigned int b, ai_real d) const + { + out = d>0.5f ? b : a; + } +}; // ! Interpolator + +template <> +struct Interpolator { + void operator () (aiVector3D& out,const aiVectorKey& a, + const aiVectorKey& b, ai_real d) const + { + Interpolator ipl; + ipl(out,a.mValue,b.mValue,d); + } +}; // ! Interpolator + +template <> +struct Interpolator { + void operator () (aiQuaternion& out, const aiQuatKey& a, + const aiQuatKey& b, ai_real d) const + { + Interpolator ipl; + ipl(out,a.mValue,b.mValue,d); + } +}; // ! Interpolator + +template <> +struct Interpolator { + void operator () (unsigned int& out, const aiMeshKey& a, + const aiMeshKey& b, ai_real d) const + { + Interpolator ipl; + ipl(out,a.mValue,b.mValue,d); + } +}; // ! Interpolator + +//! @endcond + +} // ! end namespace Assimp + +#endif // __cplusplus + +#endif // AI_ANIM_H_INC diff --git a/Assimp/include/assimp/camera.h b/Assimp/include/assimp/camera.h new file mode 100644 index 0000000..7b7bd09 --- /dev/null +++ b/Assimp/include/assimp/camera.h @@ -0,0 +1,225 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 camera.h + * @brief Defines the aiCamera data structure + */ + +#pragma once +#ifndef AI_CAMERA_H_INC +#define AI_CAMERA_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Helper structure to describe a virtual camera. + * + * Cameras have a representation in the node graph and can be animated. + * An important aspect is that the camera itself is also part of the + * scenegraph. This means, any values such as the look-at vector are not + * *absolute*, they're relative to the coordinate system defined + * by the node which corresponds to the camera. This allows for camera + * animations. For static cameras parameters like the 'look-at' or 'up' vectors + * are usually specified directly in aiCamera, but beware, they could also + * be encoded in the node transformation. The following (pseudo)code sample + * shows how to do it:

+ * @code + * // Get the camera matrix for a camera at a specific time + * // if the node hierarchy for the camera does not contain + * // at least one animated node this is a static computation + * get-camera-matrix (node sceneRoot, camera cam) : matrix + * { + * node cnd = find-node-for-camera(cam) + * matrix cmt = identity() + * + * // as usual - get the absolute camera transformation for this frame + * for each node nd in hierarchy from sceneRoot to cnd + * matrix cur + * if (is-animated(nd)) + * cur = eval-animation(nd) + * else cur = nd->mTransformation; + * cmt = mult-matrices( cmt, cur ) + * end for + * + * // now multiply with the camera's own local transform + * cam = mult-matrices (cam, get-camera-matrix(cmt) ) + * } + * @endcode + * + * @note some file formats (such as 3DS, ASE) export a "target point" - + * the point the camera is looking at (it can even be animated). Assimp + * writes the target point as a subnode of the camera's main node, + * called ".Target". However this is just additional information + * then the transformation tracks of the camera main node make the + * camera already look in the right direction. + * +*/ +struct aiCamera +{ + /** The name of the camera. + * + * There must be a node in the scenegraph with the same name. + * This node specifies the position of the camera in the scene + * hierarchy and can be animated. + */ + C_STRUCT aiString mName; + + /** Position of the camera relative to the coordinate space + * defined by the corresponding node. + * + * The default value is 0|0|0. + */ + C_STRUCT aiVector3D mPosition; + + + /** 'Up' - vector of the camera coordinate system relative to + * the coordinate space defined by the corresponding node. + * + * The 'right' vector of the camera coordinate system is + * the cross product of the up and lookAt vectors. + * The default value is 0|1|0. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mUp; + + + /** 'LookAt' - vector of the camera coordinate system relative to + * the coordinate space defined by the corresponding node. + * + * This is the viewing direction of the user. + * The default value is 0|0|1. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mLookAt; + + + /** Half horizontal field of view angle, in radians. + * + * The field of view angle is the angle between the center + * line of the screen and the left or right border. + * The default value is 1/4PI. + */ + float mHorizontalFOV; + + /** Distance of the near clipping plane from the camera. + * + * The value may not be 0.f (for arithmetic reasons to prevent + * a division through zero). The default value is 0.1f. + */ + float mClipPlaneNear; + + /** Distance of the far clipping plane from the camera. + * + * The far clipping plane must, of course, be further away than the + * near clipping plane. The default value is 1000.f. The ratio + * between the near and the far plane should not be too + * large (between 1000-10000 should be ok) to avoid floating-point + * inaccuracies which could lead to z-fighting. + */ + float mClipPlaneFar; + + + /** Screen aspect ratio. + * + * This is the ration between the width and the height of the + * screen. Typical values are 4/3, 1/2 or 1/1. This value is + * 0 if the aspect ratio is not defined in the source file. + * 0 is also the default value. + */ + float mAspect; + +#ifdef __cplusplus + + aiCamera() + : mUp (0.f,1.f,0.f) + , mLookAt (0.f,0.f,1.f) + , mHorizontalFOV (0.25f * (float)AI_MATH_PI) + , mClipPlaneNear (0.1f) + , mClipPlaneFar (1000.f) + , mAspect (0.f) + {} + + /** @brief Get a *right-handed* camera matrix from me + * @param out Camera matrix to be filled + */ + void GetCameraMatrix (aiMatrix4x4& out) const + { + /** todo: test ... should work, but i'm not absolutely sure */ + + /** We don't know whether these vectors are already normalized ...*/ + aiVector3D zaxis = mLookAt; zaxis.Normalize(); + aiVector3D yaxis = mUp; yaxis.Normalize(); + aiVector3D xaxis = mUp^mLookAt; xaxis.Normalize(); + + out.a4 = -(xaxis * mPosition); + out.b4 = -(yaxis * mPosition); + out.c4 = -(zaxis * mPosition); + + out.a1 = xaxis.x; + out.a2 = xaxis.y; + out.a3 = xaxis.z; + + out.b1 = yaxis.x; + out.b2 = yaxis.y; + out.b3 = yaxis.z; + + out.c1 = zaxis.x; + out.c2 = zaxis.y; + out.c3 = zaxis.z; + + out.d1 = out.d2 = out.d3 = 0.f; + out.d4 = 1.f; + } + +#endif +}; + + +#ifdef __cplusplus +} +#endif + +#endif // AI_CAMERA_H_INC diff --git a/Assimp/include/assimp/cexport.h b/Assimp/include/assimp/cexport.h new file mode 100644 index 0000000..44b06fb --- /dev/null +++ b/Assimp/include/assimp/cexport.h @@ -0,0 +1,263 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2011, 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 cexport.h +* @brief Defines the C-API for the Assimp export interface +*/ +#pragma once +#ifndef AI_EXPORT_H_INC +#define AI_EXPORT_H_INC + +#ifndef ASSIMP_BUILD_NO_EXPORT + +// Public ASSIMP data structures +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiScene; // aiScene.h +struct aiFileIO; // aiFileIO.h + +// -------------------------------------------------------------------------------- +/** Describes an file format which Assimp can export to. Use #aiGetExportFormatCount() to +* learn how many export formats the current Assimp build supports and #aiGetExportFormatDescription() +* to retrieve a description of an export format option. +*/ +struct aiExportFormatDesc +{ + /// a short string ID to uniquely identify the export format. Use this ID string to + /// specify which file format you want to export to when calling #aiExportScene(). + /// Example: "dae" or "obj" + const char* id; + + /// A short description of the file format to present to users. Useful if you want + /// to allow the user to select an export format. + const char* description; + + /// Recommended file extension for the exported file in lower case. + const char* fileExtension; +}; + + +// -------------------------------------------------------------------------------- +/** Returns the number of export file formats available in the current Assimp build. + * Use aiGetExportFormatDescription() to retrieve infos of a specific export format. + */ +ASSIMP_API size_t aiGetExportFormatCount(void); + + +// -------------------------------------------------------------------------------- +/** Returns a description of the nth export file format. Use #aiGetExportFormatCount() + * to learn how many export formats are supported. The description must be released by + * calling aiReleaseExportFormatDescription afterwards. + * @param pIndex Index of the export format to retrieve information for. Valid range is + * 0 to #aiGetExportFormatCount() + * @return A description of that specific export format. NULL if pIndex is out of range. + */ +ASSIMP_API const C_STRUCT aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex); + +// -------------------------------------------------------------------------------- +/** Release a description of the nth export file format. Must be returned by +* aiGetExportFormatDescription +* @param desc Pointer to the description +*/ +ASSIMP_API void aiReleaseExportFormatDescription( const C_STRUCT aiExportFormatDesc *desc ); + +// -------------------------------------------------------------------------------- +/** Create a modifiable copy of a scene. + * This is useful to import files via Assimp, change their topology and + * export them again. Since the scene returned by the various importer functions + * is const, a modifiable copy is needed. + * @param pIn Valid scene to be copied + * @param pOut Receives a modifyable copy of the scene. Use aiFreeScene() to + * delete it again. + */ +ASSIMP_API void aiCopyScene(const C_STRUCT aiScene* pIn, + C_STRUCT aiScene** pOut); + + +// -------------------------------------------------------------------------------- +/** Frees a scene copy created using aiCopyScene() */ +ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn); + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format and writes the result file(s) to disk. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* The scene is expected to conform to Assimp's Importer output format as specified +* in the @link data Data Structures Page @endlink. In short, this means the model data +* should use a right-handed coordinate systems, face winding should be counter-clockwise +* and the UV coordinate origin is assumed to be in the upper left. If your input data +* uses different conventions, have a look at the last parameter. +* @param pFormatId ID string to specify to which format you want to export to. Use +* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* @param pFileName Output file to write +* @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated +* flags, but in reality only a subset of them makes sense here. Specifying +* 'preprocessing' flags is useful if the input scene does not conform to +* Assimp's default conventions as specified in the @link data Data Structures Page @endlink. +* In short, this means the geometry data should use a right-handed coordinate systems, face +* winding should be counter-clockwise and the UV coordinate origin is assumed to be in +* the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and +* #aiProcess_FlipWindingOrder flags are used in the import side to allow users +* to have those defaults automatically adapted to their conventions. Specifying those flags +* for exporting has the opposite effect, respectively. Some other of the +* #aiPostProcessSteps enumerated values may be useful as well, but you'll need +* to try out what their effect on the exported file is. Many formats impose +* their own restrictions on the structure of the geometry stored therein, +* so some preprocessing may have little or no effect at all, or may be +* redundant as exporters would apply them anyhow. A good example +* is triangulation - whilst you can enforce it by specifying +* the #aiProcess_Triangulate flag, most export formats support only +* triangulate data so they would run the step anyway. +* +* If assimp detects that the input scene was directly taken from the importer side of +* the library (i.e. not copied using aiCopyScene and potetially modified afterwards), +* any postprocessing steps already applied to the scene will not be applied again, unless +* they show non-idempotent behaviour (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and +* #aiProcess_FlipWindingOrder). +* @return a status code indicating the result of the export +* @note Use aiCopyScene() to get a modifiable copy of a previously +* imported scene. +*/ +ASSIMP_API aiReturn aiExportScene( const C_STRUCT aiScene* pScene, + const char* pFormatId, + const char* pFileName, + unsigned int pPreprocessing); + + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format using custom IO logic supplied by you. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* @param pFormatId ID string to specify to which format you want to export to. Use +* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available. +* @param pFileName Output file to write +* @param pIO custom IO implementation to be used. Use this if you use your own storage methods. +* If none is supplied, a default implementation using standard file IO is used. Note that +* #aiExportSceneToBlob is provided as convenience function to export to memory buffers. +* @param pPreprocessing Please see the documentation for #aiExportScene +* @return a status code indicating the result of the export +* @note Include for the definition of #aiFileIO. +* @note Use aiCopyScene() to get a modifiable copy of a previously +* imported scene. +*/ +ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene, + const char* pFormatId, + const char* pFileName, + C_STRUCT aiFileIO* pIO, + unsigned int pPreprocessing ); + + +// -------------------------------------------------------------------------------- +/** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an +* exported scene. The memory referred by this structure is owned by Assimp. +* to free its resources. Don't try to free the memory on your side - it will crash for most build configurations +* due to conflicting heaps. +* +* Blobs can be nested - each blob may reference another blob, which may in turn reference another blob and so on. +* This is used when exporters write more than one output file for a given #aiScene. See the remarks for +* #aiExportDataBlob::name for more information. +*/ +struct aiExportDataBlob +{ + /// Size of the data in bytes + size_t size; + + /// The data. + void* data; + + /** Name of the blob. An empty string always + indicates the first (and primary) blob, + which contains the actual file data. + Any other blobs are auxiliary files produced + by exporters (i.e. material files). Existence + of such files depends on the file format. Most + formats don't split assets across multiple files. + + If used, blob names usually contain the file + extension that should be used when writing + the data to disc. + */ + C_STRUCT aiString name; + + /** Pointer to the next blob in the chain or NULL if there is none. */ + C_STRUCT aiExportDataBlob * next; + +#ifdef __cplusplus + /// Default constructor + aiExportDataBlob() { size = 0; data = next = NULL; } + /// Releases the data + ~aiExportDataBlob() { delete [] static_cast( data ); delete next; } + +private: + // no copying + aiExportDataBlob(const aiExportDataBlob& ); + aiExportDataBlob& operator= (const aiExportDataBlob& ); +#endif // __cplusplus +}; + +// -------------------------------------------------------------------------------- +/** Exports the given scene to a chosen file format. Returns the exported data as a binary blob which +* you can write into a file or something. When you're done with the data, use #aiReleaseExportBlob() +* to free the resources associated with the export. +* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function. +* @param pFormatId ID string to specify to which format you want to export to. Use +* #aiGetExportFormatCount() / #aiGetExportFormatDescription() to learn which export formats are available. +* @param pPreprocessing Please see the documentation for #aiExportScene +* @return the exported data or NULL in case of error +*/ +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const C_STRUCT aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ); + + +// -------------------------------------------------------------------------------- +/** Releases the memory associated with the given exported data. Use this function to free a data blob +* returned by aiExportScene(). +* @param pData the data blob returned by #aiExportSceneToBlob +*/ +ASSIMP_API void aiReleaseExportBlob( const C_STRUCT aiExportDataBlob* pData ); + +#ifdef __cplusplus +} +#endif + +#endif // ASSIMP_BUILD_NO_EXPORT +#endif // AI_EXPORT_H_INC diff --git a/Assimp/include/assimp/cfileio.h b/Assimp/include/assimp/cfileio.h new file mode 100644 index 0000000..63eeb59 --- /dev/null +++ b/Assimp/include/assimp/cfileio.h @@ -0,0 +1,137 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 cfileio.h + * @brief Defines generic C routines to access memory-mapped files + */ +#pragma once +#ifndef AI_FILEIO_H_INC +#define AI_FILEIO_H_INC + +#include +#ifdef __cplusplus +extern "C" { +#endif +struct aiFileIO; +struct aiFile; + +// aiFile callbacks +typedef size_t (*aiFileWriteProc) (C_STRUCT aiFile*, const char*, size_t, size_t); +typedef size_t (*aiFileReadProc) (C_STRUCT aiFile*, char*, size_t,size_t); +typedef size_t (*aiFileTellProc) (C_STRUCT aiFile*); +typedef void (*aiFileFlushProc) (C_STRUCT aiFile*); +typedef C_ENUM aiReturn (*aiFileSeek) (C_STRUCT aiFile*, size_t, C_ENUM aiOrigin); + +// aiFileIO callbacks +typedef C_STRUCT aiFile* (*aiFileOpenProc) (C_STRUCT aiFileIO*, const char*, const char*); +typedef void (*aiFileCloseProc) (C_STRUCT aiFileIO*, C_STRUCT aiFile*); + +// Represents user-defined data +typedef char* aiUserData; + +// ---------------------------------------------------------------------------------- +/** @brief C-API: File system callbacks + * + * Provided are functions to open and close files. Supply a custom structure to + * the import function. If you don't, a default implementation is used. Use custom + * file systems to enable reading from other sources, such as ZIPs + * or memory locations. */ +struct aiFileIO +{ + /** Function used to open a new file + */ + aiFileOpenProc OpenProc; + + /** Function used to close an existing file + */ + aiFileCloseProc CloseProc; + + /** User-defined, opaque data */ + aiUserData UserData; +}; + +// ---------------------------------------------------------------------------------- +/** @brief C-API: File callbacks + * + * Actually, it's a data structure to wrap a set of fXXXX (e.g fopen) + * replacement functions. + * + * The default implementation of the functions utilizes the fXXX functions from + * the CRT. However, you can supply a custom implementation to Assimp by + * delivering a custom aiFileIO. Use this to enable reading from other sources, + * such as ZIP archives or memory locations. */ +struct aiFile +{ + /** Callback to read from a file */ + aiFileReadProc ReadProc; + + /** Callback to write to a file */ + aiFileWriteProc WriteProc; + + /** Callback to retrieve the current position of + * the file cursor (ftell()) + */ + aiFileTellProc TellProc; + + /** Callback to retrieve the size of the file, + * in bytes + */ + aiFileTellProc FileSizeProc; + + /** Callback to set the current position + * of the file cursor (fseek()) + */ + aiFileSeek SeekProc; + + /** Callback to flush the file contents + */ + aiFileFlushProc FlushProc; + + /** User-defined, opaque data + */ + aiUserData UserData; +}; + +#ifdef __cplusplus +} +#endif +#endif // AI_FILEIO_H_INC diff --git a/Assimp/include/assimp/cimport.h b/Assimp/include/assimp/cimport.h new file mode 100644 index 0000000..8aa125c --- /dev/null +++ b/Assimp/include/assimp/cimport.h @@ -0,0 +1,564 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 cimport.h + * @brief Defines the C-API to the Open Asset Import Library. + */ +#pragma once +#ifndef AI_ASSIMP_H_INC +#define AI_ASSIMP_H_INC + +#include +#include "importerdesc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct aiScene; // aiScene.h +struct aiFileIO; // aiFileIO.h +typedef void (*aiLogStreamCallback)(const char* /* message */, char* /* user */); + +// -------------------------------------------------------------------------------- +/** C-API: Represents a log stream. A log stream receives all log messages and + * streams them _somewhere_. + * @see aiGetPredefinedLogStream + * @see aiAttachLogStream + * @see aiDetachLogStream */ +// -------------------------------------------------------------------------------- +struct aiLogStream +{ + /** callback to be called */ + aiLogStreamCallback callback; + + /** user data to be passed to the callback */ + char* user; +}; + + +// -------------------------------------------------------------------------------- +/** C-API: Represents an opaque set of settings to be used during importing. + * @see aiCreatePropertyStore + * @see aiReleasePropertyStore + * @see aiImportFileExWithProperties + * @see aiSetPropertyInteger + * @see aiSetPropertyFloat + * @see aiSetPropertyString + * @see aiSetPropertyMatrix + */ +// -------------------------------------------------------------------------------- +struct aiPropertyStore { char sentinel; }; + +/** Our own C boolean type */ +typedef int aiBool; + +#define AI_FALSE 0 +#define AI_TRUE 1 + +// -------------------------------------------------------------------------------- +/** Reads the given file and returns its content. + * + * If the call succeeds, the imported data is returned in an aiScene structure. + * The data is intended to be read-only, it stays property of the ASSIMP + * library and will be stable until aiReleaseImport() is called. After you're + * done with it, call aiReleaseImport() to free the resources associated with + * this file. If the import fails, NULL is returned instead. Call + * aiGetErrorString() to retrieve a human-readable error text. + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @return Pointer to the imported data or NULL if the import failed. + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFile( + const char* pFile, + unsigned int pFlags); + +// -------------------------------------------------------------------------------- +/** Reads the given file using user-defined I/O functions and returns + * its content. + * + * If the call succeeds, the imported data is returned in an aiScene structure. + * The data is intended to be read-only, it stays property of the ASSIMP + * library and will be stable until aiReleaseImport() is called. After you're + * done with it, call aiReleaseImport() to free the resources associated with + * this file. If the import fails, NULL is returned instead. Call + * aiGetErrorString() to retrieve a human-readable error text. + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @param pFS aiFileIO structure. Will be used to open the model file itself + * and any other files the loader needs to open. Pass NULL to use the default + * implementation. + * @return Pointer to the imported data or NULL if the import failed. + * @note Include for the definition of #aiFileIO. + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( + const char* pFile, + unsigned int pFlags, + C_STRUCT aiFileIO* pFS); + +// -------------------------------------------------------------------------------- +/** Same as #aiImportFileEx, but adds an extra parameter containing importer settings. + * + * @param pFile Path and filename of the file to be imported, + * expected to be a null-terminated c-string. NULL is not a valid value. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @param pFS aiFileIO structure. Will be used to open the model file itself + * and any other files the loader needs to open. Pass NULL to use the default + * implementation. + * @param pProps #aiPropertyStore instance containing import settings. + * @return Pointer to the imported data or NULL if the import failed. + * @note Include for the definition of #aiFileIO. + * @see aiImportFileEx + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileExWithProperties( + const char* pFile, + unsigned int pFlags, + C_STRUCT aiFileIO* pFS, + const C_STRUCT aiPropertyStore* pProps); + +// -------------------------------------------------------------------------------- +/** Reads the given file from a given memory buffer, + * + * If the call succeeds, the contents of the file are returned as a pointer to an + * aiScene object. The returned data is intended to be read-only, the importer keeps + * ownership of the data and will destroy it upon destruction. If the import fails, + * NULL is returned. + * A human-readable error description can be retrieved by calling aiGetErrorString(). + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #aiApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non empty string, + * the library looks for a loader to support the file extension specified by pHint + * and passes the file to the first matching loader. If this loader is unable to + * completely the request, the library continues and tries to determine the file + * format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @return A pointer to the imported data, NULL if the import failed. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular aiImportFileEx()/aiImportFileExWithProperties() API. + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemory( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint); + +// -------------------------------------------------------------------------------- +/** Same as #aiImportFileFromMemory, but adds an extra parameter containing importer settings. + * + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #aiApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non empty string, + * the library looks for a loader to support the file extension specified by pHint + * and passes the file to the first matching loader. If this loader is unable to + * completely the request, the library continues and tries to determine the file + * format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @param pProps #aiPropertyStore instance containing import settings. + * @return A pointer to the imported data, NULL if the import failed. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular aiImportFileEx()/aiImportFileExWithProperties() API. + * @see aiImportFileFromMemory + */ +ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint, + const C_STRUCT aiPropertyStore* pProps); + +// -------------------------------------------------------------------------------- +/** Apply post-processing to an already-imported scene. + * + * This is strictly equivalent to calling #aiImportFile()/#aiImportFileEx with the + * same flags. However, you can use this separate function to inspect the imported + * scene first to fine-tune your post-processing setup. + * @param pScene Scene to work on. + * @param pFlags Provide a bitwise combination of the #aiPostProcessSteps flags. + * @return A pointer to the post-processed data. Post processing is done in-place, + * meaning this is still the same #aiScene which you passed for pScene. However, + * _if_ post-processing failed, the scene could now be NULL. That's quite a rare + * case, post processing steps are not really designed to 'fail'. To be exact, + * the #aiProcess_ValidateDataStructure flag is currently the only post processing step + * which can actually cause the scene to be reset to NULL. + */ +ASSIMP_API const C_STRUCT aiScene* aiApplyPostProcessing( + const C_STRUCT aiScene* pScene, + unsigned int pFlags); + +// -------------------------------------------------------------------------------- +/** Get one of the predefine log streams. This is the quick'n'easy solution to + * access Assimp's log system. Attaching a log stream can slightly reduce Assimp's + * overall import performance. + * + * Usage is rather simple (this will stream the log to a file, named log.txt, and + * the stdout stream of the process: + * @code + * struct aiLogStream c; + * c = aiGetPredefinedLogStream(aiDefaultLogStream_FILE,"log.txt"); + * aiAttachLogStream(&c); + * c = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL); + * aiAttachLogStream(&c); + * @endcode + * + * @param pStreams One of the #aiDefaultLogStream enumerated values. + * @param file Solely for the #aiDefaultLogStream_FILE flag: specifies the file to write to. + * Pass NULL for all other flags. + * @return The log stream. callback is set to NULL if something went wrong. + */ +ASSIMP_API C_STRUCT aiLogStream aiGetPredefinedLogStream( + C_ENUM aiDefaultLogStream pStreams, + const char* file); + +// -------------------------------------------------------------------------------- +/** Attach a custom log stream to the libraries' logging system. + * + * Attaching a log stream can slightly reduce Assimp's overall import + * performance. Multiple log-streams can be attached. + * @param stream Describes the new log stream. + * @note To ensure proper destruction of the logging system, you need to manually + * call aiDetachLogStream() on every single log stream you attach. + * Alternatively (for the lazy folks) #aiDetachAllLogStreams is provided. + */ +ASSIMP_API void aiAttachLogStream( + const C_STRUCT aiLogStream* stream); + +// -------------------------------------------------------------------------------- +/** Enable verbose logging. Verbose logging includes debug-related stuff and + * detailed import statistics. This can have severe impact on import performance + * and memory consumption. However, it might be useful to find out why a file + * didn't read correctly. + * @param d AI_TRUE or AI_FALSE, your decision. + */ +ASSIMP_API void aiEnableVerboseLogging(aiBool d); + +// -------------------------------------------------------------------------------- +/** Detach a custom log stream from the libraries' logging system. + * + * This is the counterpart of #aiAttachLogStream. If you attached a stream, + * don't forget to detach it again. + * @param stream The log stream to be detached. + * @return AI_SUCCESS if the log stream has been detached successfully. + * @see aiDetachAllLogStreams + */ +ASSIMP_API C_ENUM aiReturn aiDetachLogStream( + const C_STRUCT aiLogStream* stream); + +// -------------------------------------------------------------------------------- +/** Detach all active log streams from the libraries' logging system. + * This ensures that the logging system is terminated properly and all + * resources allocated by it are actually freed. If you attached a stream, + * don't forget to detach it again. + * @see aiAttachLogStream + * @see aiDetachLogStream + */ +ASSIMP_API void aiDetachAllLogStreams(void); + +// -------------------------------------------------------------------------------- +/** Releases all resources associated with the given import process. + * + * Call this function after you're done with the imported data. + * @param pScene The imported data to release. NULL is a valid value. + */ +ASSIMP_API void aiReleaseImport( + const C_STRUCT aiScene* pScene); + +// -------------------------------------------------------------------------------- +/** Returns the error text of the last failed import process. + * + * @return A textual description of the error that occurred at the last + * import process. NULL if there was no error. There can't be an error if you + * got a non-NULL #aiScene from #aiImportFile/#aiImportFileEx/#aiApplyPostProcessing. + */ +ASSIMP_API const char* aiGetErrorString(void); + +// -------------------------------------------------------------------------------- +/** Returns whether a given file extension is supported by ASSIMP + * + * @param szExtension Extension for which the function queries support for. + * Must include a leading dot '.'. Example: ".3ds", ".md3" + * @return AI_TRUE if the file extension is supported. + */ +ASSIMP_API aiBool aiIsExtensionSupported( + const char* szExtension); + +// -------------------------------------------------------------------------------- +/** Get a list of all file extensions supported by ASSIMP. + * + * If a file extension is contained in the list this does, of course, not + * mean that ASSIMP is able to load all files with this extension. + * @param szOut String to receive the extension list. + * Format of the list: "*.3ds;*.obj;*.dae". NULL is not a valid parameter. + */ +ASSIMP_API void aiGetExtensionList( + C_STRUCT aiString* szOut); + +// -------------------------------------------------------------------------------- +/** Get the approximated storage required by an imported asset + * @param pIn Input asset. + * @param in Data structure to be filled. + */ +ASSIMP_API void aiGetMemoryRequirements( + const C_STRUCT aiScene* pIn, + C_STRUCT aiMemoryInfo* in); + + + +// -------------------------------------------------------------------------------- +/** Create an empty property store. Property stores are used to collect import + * settings. + * @return New property store. Property stores need to be manually destroyed using + * the #aiReleasePropertyStore API function. + */ +ASSIMP_API C_STRUCT aiPropertyStore* aiCreatePropertyStore(void); + +// -------------------------------------------------------------------------------- +/** Delete a property store. + * @param p Property store to be deleted. + */ +ASSIMP_API void aiReleasePropertyStore(C_STRUCT aiPropertyStore* p); + +// -------------------------------------------------------------------------------- +/** Set an integer property. + * + * This is the C-version of #Assimp::Importer::SetPropertyInteger(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param value New value for the property + */ +ASSIMP_API void aiSetImportPropertyInteger( + C_STRUCT aiPropertyStore* store, + const char* szName, + int value); + +// -------------------------------------------------------------------------------- +/** Set a floating-point property. + * + * This is the C-version of #Assimp::Importer::SetPropertyFloat(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param value New value for the property + */ +ASSIMP_API void aiSetImportPropertyFloat( + C_STRUCT aiPropertyStore* store, + const char* szName, + ai_real value); + +// -------------------------------------------------------------------------------- +/** Set a string property. + * + * This is the C-version of #Assimp::Importer::SetPropertyString(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param st New value for the property + */ +ASSIMP_API void aiSetImportPropertyString( + C_STRUCT aiPropertyStore* store, + const char* szName, + const C_STRUCT aiString* st); + +// -------------------------------------------------------------------------------- +/** Set a matrix property. + * + * This is the C-version of #Assimp::Importer::SetPropertyMatrix(). In the C + * interface, properties are always shared by all imports. It is not possible to + * specify them per import. + * + * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store. + * @param szName Name of the configuration property to be set. All supported + * public properties are defined in the config.h header file (AI_CONFIG_XXX). + * @param mat New value for the property + */ +ASSIMP_API void aiSetImportPropertyMatrix( + C_STRUCT aiPropertyStore* store, + const char* szName, + const C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Construct a quaternion from a 3x3 rotation matrix. + * @param quat Receives the output quaternion. + * @param mat Matrix to 'quaternionize'. + * @see aiQuaternion(const aiMatrix3x3& pRotMatrix) + */ +ASSIMP_API void aiCreateQuaternionFromMatrix( + C_STRUCT aiQuaternion* quat, + const C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Decompose a transformation matrix into its rotational, translational and + * scaling components. + * + * @param mat Matrix to decompose + * @param scaling Receives the scaling component + * @param rotation Receives the rotational component + * @param position Receives the translational component. + * @see aiMatrix4x4::Decompose (aiVector3D&, aiQuaternion&, aiVector3D&) const; + */ +ASSIMP_API void aiDecomposeMatrix( + const C_STRUCT aiMatrix4x4* mat, + C_STRUCT aiVector3D* scaling, + C_STRUCT aiQuaternion* rotation, + C_STRUCT aiVector3D* position); + +// -------------------------------------------------------------------------------- +/** Transpose a 4x4 matrix. + * @param mat Pointer to the matrix to be transposed + */ +ASSIMP_API void aiTransposeMatrix4( + C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Transpose a 3x3 matrix. + * @param mat Pointer to the matrix to be transposed + */ +ASSIMP_API void aiTransposeMatrix3( + C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Transform a vector by a 3x3 matrix + * @param vec Vector to be transformed. + * @param mat Matrix to transform the vector with. + */ +ASSIMP_API void aiTransformVecByMatrix3( + C_STRUCT aiVector3D* vec, + const C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Transform a vector by a 4x4 matrix + * @param vec Vector to be transformed. + * @param mat Matrix to transform the vector with. + */ +ASSIMP_API void aiTransformVecByMatrix4( + C_STRUCT aiVector3D* vec, + const C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Multiply two 4x4 matrices. + * @param dst First factor, receives result. + * @param src Matrix to be multiplied with 'dst'. + */ +ASSIMP_API void aiMultiplyMatrix4( + C_STRUCT aiMatrix4x4* dst, + const C_STRUCT aiMatrix4x4* src); + +// -------------------------------------------------------------------------------- +/** Multiply two 3x3 matrices. + * @param dst First factor, receives result. + * @param src Matrix to be multiplied with 'dst'. + */ +ASSIMP_API void aiMultiplyMatrix3( + C_STRUCT aiMatrix3x3* dst, + const C_STRUCT aiMatrix3x3* src); + +// -------------------------------------------------------------------------------- +/** Get a 3x3 identity matrix. + * @param mat Matrix to receive its personal identity + */ +ASSIMP_API void aiIdentityMatrix3( + C_STRUCT aiMatrix3x3* mat); + +// -------------------------------------------------------------------------------- +/** Get a 4x4 identity matrix. + * @param mat Matrix to receive its personal identity + */ +ASSIMP_API void aiIdentityMatrix4( + C_STRUCT aiMatrix4x4* mat); + +// -------------------------------------------------------------------------------- +/** Returns the number of import file formats available in the current Assimp build. + * Use aiGetImportFormatDescription() to retrieve infos of a specific import format. + */ +ASSIMP_API size_t aiGetImportFormatCount(void); + +// -------------------------------------------------------------------------------- +/** Returns a description of the nth import file format. Use #aiGetImportFormatCount() + * to learn how many import formats are supported. + * @param pIndex Index of the import format to retrieve information for. Valid range is + * 0 to #aiGetImportFormatCount() + * @return A description of that specific import format. NULL if pIndex is out of range. + */ +ASSIMP_API const C_STRUCT aiImporterDesc* aiGetImportFormatDescription( size_t pIndex); +#ifdef __cplusplus +} +#endif + +#endif // AI_ASSIMP_H_INC diff --git a/Assimp/include/assimp/color4.h b/Assimp/include/assimp/color4.h new file mode 100644 index 0000000..48e7aad --- /dev/null +++ b/Assimp/include/assimp/color4.h @@ -0,0 +1,104 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 color4.h + * @brief RGBA color structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_COLOR4D_H_INC +#define AI_COLOR4D_H_INC + +#include "defs.h" + +#ifdef __cplusplus + +// ---------------------------------------------------------------------------------- +/** Represents a color in Red-Green-Blue space including an +* alpha component. Color values range from 0 to 1. */ +// ---------------------------------------------------------------------------------- +template +class aiColor4t +{ +public: + aiColor4t () : r(), g(), b(), a() {} + aiColor4t (TReal _r, TReal _g, TReal _b, TReal _a) + : r(_r), g(_g), b(_b), a(_a) {} + explicit aiColor4t (TReal _r) : r(_r), g(_r), b(_r), a(_r) {} + aiColor4t (const aiColor4t& o) + : r(o.r), g(o.g), b(o.b), a(o.a) {} + +public: + // combined operators + const aiColor4t& operator += (const aiColor4t& o); + const aiColor4t& operator -= (const aiColor4t& o); + const aiColor4t& operator *= (TReal f); + const aiColor4t& operator /= (TReal f); + +public: + // comparison + bool operator == (const aiColor4t& other) const; + bool operator != (const aiColor4t& other) const; + bool operator < (const aiColor4t& other) const; + + // color tuple access, rgba order + inline TReal operator[](unsigned int i) const; + inline TReal& operator[](unsigned int i); + + /** check whether a color is (close to) black */ + inline bool IsBlack() const; + +public: + + // Red, green, blue and alpha color values + TReal r, g, b, a; +}; // !struct aiColor4D + +typedef aiColor4t aiColor4D; + +#else + +struct aiColor4D { + ai_real r, g, b, a; +}; + +#endif // __cplusplus + +#endif // AI_COLOR4D_H_INC diff --git a/Assimp/include/assimp/color4.inl b/Assimp/include/assimp/color4.inl new file mode 100644 index 0000000..b242c4e --- /dev/null +++ b/Assimp/include/assimp/color4.inl @@ -0,0 +1,204 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 color4.inl + * @brief Inline implementation of aiColor4t operators + */ +#pragma once +#ifndef AI_COLOR4D_INL_INC +#define AI_COLOR4D_INL_INC + +#ifdef __cplusplus +#include "color4.h" + +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiColor4t& aiColor4t::operator += (const aiColor4t& o) { + r += o.r; g += o.g; b += o.b; a += o.a; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiColor4t& aiColor4t::operator -= (const aiColor4t& o) { + r -= o.r; g -= o.g; b -= o.b; a -= o.a; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiColor4t& aiColor4t::operator *= (TReal f) { + r *= f; g *= f; b *= f; a *= f; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiColor4t& aiColor4t::operator /= (TReal f) { + r /= f; g /= f; b /= f; a /= f; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE TReal aiColor4t::operator[](unsigned int i) const { + switch ( i ) { + case 0: + return r; + case 1: + return g; + case 2: + return b; + default: + break; + } + return r; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE TReal& aiColor4t::operator[](unsigned int i) { + switch ( i ) { + case 0: + return r; + case 1: + return g; + case 2: + return b; + default: + break; + } + return r; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool aiColor4t::operator== (const aiColor4t& other) const { + return r == other.r && g == other.g && b == other.b && a == other.a; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool aiColor4t::operator!= (const aiColor4t& other) const { + return r != other.r || g != other.g || b != other.b || a != other.a; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool aiColor4t::operator< (const aiColor4t& other) const { + return r < other.r || ( + r == other.r && ( + g < other.g || ( + g == other.g && ( + b < other.b || ( + b == other.b && ( + a < other.a + ) + ) + ) + ) + ) + ); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator + (const aiColor4t& v1, const aiColor4t& v2) { + return aiColor4t( v1.r + v2.r, v1.g + v2.g, v1.b + v2.b, v1.a + v2.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator - (const aiColor4t& v1, const aiColor4t& v2) { + return aiColor4t( v1.r - v2.r, v1.g - v2.g, v1.b - v2.b, v1.a - v2.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator * (const aiColor4t& v1, const aiColor4t& v2) { + return aiColor4t( v1.r * v2.r, v1.g * v2.g, v1.b * v2.b, v1.a * v2.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator / (const aiColor4t& v1, const aiColor4t& v2) { + return aiColor4t( v1.r / v2.r, v1.g / v2.g, v1.b / v2.b, v1.a / v2.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator * ( TReal f, const aiColor4t& v) { + return aiColor4t( f*v.r, f*v.g, f*v.b, f*v.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator * ( const aiColor4t& v, TReal f) { + return aiColor4t( f*v.r, f*v.g, f*v.b, f*v.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator / ( const aiColor4t& v, TReal f) { + return v * (1/f); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator / ( TReal f,const aiColor4t& v) { + return aiColor4t(f,f,f,f)/v; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator + ( const aiColor4t& v, TReal f) { + return aiColor4t( f+v.r, f+v.g, f+v.b, f+v.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator - ( const aiColor4t& v, TReal f) { + return aiColor4t( v.r-f, v.g-f, v.b-f, v.a-f); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator + ( TReal f, const aiColor4t& v) { + return aiColor4t( f+v.r, f+v.g, f+v.b, f+v.a); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiColor4t operator - ( TReal f, const aiColor4t& v) { + return aiColor4t( f-v.r, f-v.g, f-v.b, f-v.a); +} + +// ------------------------------------------------------------------------------------------------ +template +inline bool aiColor4t :: IsBlack() const { + // The alpha component doesn't care here. black is black. + static const TReal epsilon = 10e-3f; + return std::fabs( r ) < epsilon && std::fabs( g ) < epsilon && std::fabs( b ) < epsilon; +} + +#endif // __cplusplus +#endif // AI_VECTOR3D_INL_INC diff --git a/Assimp/include/assimp/config.h b/Assimp/include/assimp/config.h new file mode 100644 index 0000000..3323152 --- /dev/null +++ b/Assimp/include/assimp/config.h @@ -0,0 +1,963 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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. + * + *

+ * 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: + * "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'". + * 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.
+ * 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: + * "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'". + * If a node matches on of these names, it will not be modified or + * removed by the postprocessing step.
+ * 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_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 search for embedded loaded textures, where no embedded texture data is provided. +* +* The default value is false (0) +* Property type: bool +*/ +#define AI_CONFIG_IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES \ + "IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES" + +// --------------------------------------------------------------------------- +/** @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" + + +// --------------------------------------------------------------------------- +/** @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 + * any_path/models/any_q3_subdir/model_name/file_name.md3 is + * loaded, the library tries to locate the corresponding shader file in + * any_path/scripts/model_name.shader. 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 + * IMPORT_MD3_SHADER_SRC/model_name.shader first, IMPORT_MD3_SHADER_SRC/file_name.shader + * 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.
+ * 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)
+ * 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.
+ * 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, .material and + * lastly the material name defined by this config property. + *
+ * 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. + *
+ * 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.
+ * 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" + +// ---------- 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" + +/** + * @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. + */ + +/* #undef ASSIMP_DOUBLE_PRECISION */ + +#endif // !! AI_CONFIG_H_INC diff --git a/Assimp/include/assimp/config.h.in b/Assimp/include/assimp/config.h.in new file mode 100644 index 0000000..1dabae7 --- /dev/null +++ b/Assimp/include/assimp/config.h.in @@ -0,0 +1,963 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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. + * + *

+ * 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: + * "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'". + * 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.
+ * 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: + * "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'". + * If a node matches on of these names, it will not be modified or + * removed by the postprocessing step.
+ * 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_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 search for embedded loaded textures, where no embedded texture data is provided. +* +* The default value is false (0) +* Property type: bool +*/ +#define AI_CONFIG_IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES \ + "IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES" + +// --------------------------------------------------------------------------- +/** @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" + + +// --------------------------------------------------------------------------- +/** @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 + * any_path/models/any_q3_subdir/model_name/file_name.md3 is + * loaded, the library tries to locate the corresponding shader file in + * any_path/scripts/model_name.shader. 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 + * IMPORT_MD3_SHADER_SRC/model_name.shader first, IMPORT_MD3_SHADER_SRC/file_name.shader + * 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.
+ * 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)
+ * 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.
+ * 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, .material and + * lastly the material name defined by this config property. + *
+ * 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. + *
+ * 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.
+ * 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" + +// ---------- 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" + +/** + * @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 diff --git a/Assimp/include/assimp/defs.h b/Assimp/include/assimp/defs.h new file mode 100644 index 0000000..20b234f --- /dev/null +++ b/Assimp/include/assimp/defs.h @@ -0,0 +1,290 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 defs.h + * @brief Assimp build configuration setup. See the notes in the comment + * blocks to find out how to customize _your_ Assimp build. + */ + +#pragma once +#ifndef AI_DEFINES_H_INC +#define AI_DEFINES_H_INC + +#include + + ////////////////////////////////////////////////////////////////////////// + /* Define ASSIMP_BUILD_NO_XX_IMPORTER to disable a specific + * file format loader. The loader is be excluded from the + * build in this case. 'XX' stands for the most common file + * extension of the file format. E.g.: + * ASSIMP_BUILD_NO_X_IMPORTER disables the X loader. + * + * If you're unsure about that, take a look at the implementation of the + * import plugin you wish to disable. You'll find the right define in the + * first lines of the corresponding unit. + * + * Other (mixed) configuration switches are listed here: + * ASSIMP_BUILD_NO_COMPRESSED_X + * - Disable support for compressed X files (zip) + * ASSIMP_BUILD_NO_COMPRESSED_BLEND + * - Disable support for compressed Blender files (zip) + * ASSIMP_BUILD_NO_COMPRESSED_IFC + * - Disable support for IFCZIP files (unzip) + */ + ////////////////////////////////////////////////////////////////////////// + +#ifndef ASSIMP_BUILD_NO_COMPRESSED_X +# define ASSIMP_BUILD_NEED_Z_INFLATE +#endif + +#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND +# define ASSIMP_BUILD_NEED_Z_INFLATE +#endif + +#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC +# define ASSIMP_BUILD_NEED_Z_INFLATE +# define ASSIMP_BUILD_NEED_UNZIP +#endif + +#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER +# define ASSIMP_BUILD_NEED_Z_INFLATE +# define ASSIMP_BUILD_NEED_UNZIP +#endif + + ////////////////////////////////////////////////////////////////////////// + /* Define ASSIMP_BUILD_NO_XX_PROCESS to disable a specific + * post processing step. This is the current list of process names ('XX'): + * CALCTANGENTS + * JOINVERTICES + * TRIANGULATE + * GENFACENORMALS + * GENVERTEXNORMALS + * REMOVEVC + * SPLITLARGEMESHES + * PRETRANSFORMVERTICES + * LIMITBONEWEIGHTS + * VALIDATEDS + * IMPROVECACHELOCALITY + * FIXINFACINGNORMALS + * REMOVE_REDUNDANTMATERIALS + * OPTIMIZEGRAPH + * SORTBYPTYPE + * FINDINVALIDDATA + * TRANSFORMTEXCOORDS + * GENUVCOORDS + * ENTITYMESHBUILDER + * MAKELEFTHANDED + * FLIPUVS + * FLIPWINDINGORDER + * OPTIMIZEMESHES + * OPTIMIZEANIMS + * OPTIMIZEGRAPH + * GENENTITYMESHES + * FIXTEXTUREPATHS */ + ////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +# undef ASSIMP_API + + ////////////////////////////////////////////////////////////////////////// + /* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */ + ////////////////////////////////////////////////////////////////////////// +# ifdef ASSIMP_BUILD_DLL_EXPORT +# define ASSIMP_API __declspec(dllexport) +# define ASSIMP_API_WINONLY __declspec(dllexport) +# pragma warning (disable : 4251) + + ////////////////////////////////////////////////////////////////////////// + /* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in + * an external DLL under Windows. Default is static linkage. */ + ////////////////////////////////////////////////////////////////////////// +# elif (defined ASSIMP_DLL) +# define ASSIMP_API __declspec(dllimport) +# define ASSIMP_API_WINONLY __declspec(dllimport) +# else +# define ASSIMP_API +# define ASSIMP_API_WINONLY +# endif + + /* Force the compiler to inline a function, if possible + */ +# define AI_FORCE_INLINE __forceinline + + /* Tells the compiler that a function never returns. Used in code analysis + * to skip dead paths (e.g. after an assertion evaluated to false). */ +# define AI_WONT_RETURN __declspec(noreturn) + +#elif defined(SWIG) + + /* Do nothing, the relevant defines are all in AssimpSwigPort.i */ + +#else + +# define AI_WONT_RETURN + +# define ASSIMP_API __attribute__ ((visibility("default"))) +# define ASSIMP_API_WINONLY +# define AI_FORCE_INLINE inline +#endif // (defined _MSC_VER) + +#ifdef __GNUC__ +# define AI_WONT_RETURN_SUFFIX __attribute__((noreturn)) +#else +# define AI_WONT_RETURN_SUFFIX +#endif // (defined __clang__) + +#ifdef __cplusplus + /* No explicit 'struct' and 'enum' tags for C++, this keeps showing up + * in doxydocs. + */ +# define C_STRUCT +# define C_ENUM +#else + ////////////////////////////////////////////////////////////////////////// + /* To build the documentation, make sure ASSIMP_DOXYGEN_BUILD + * is defined by Doxygen's preprocessor. The corresponding + * entries in the DOXYFILE are: */ + ////////////////////////////////////////////////////////////////////////// +#if 0 + ENABLE_PREPROCESSING = YES + MACRO_EXPANSION = YES + EXPAND_ONLY_PREDEF = YES + SEARCH_INCLUDES = YES + INCLUDE_PATH = + INCLUDE_FILE_PATTERNS = + PREDEFINED = ASSIMP_DOXYGEN_BUILD=1 + EXPAND_AS_DEFINED = C_STRUCT C_ENUM + SKIP_FUNCTION_MACROS = YES +#endif + ////////////////////////////////////////////////////////////////////////// + /* Doxygen gets confused if we use c-struct typedefs to avoid + * the explicit 'struct' notation. This trick here has the same + * effect as the TYPEDEF_HIDES_STRUCT option, but we don't need + * to typedef all structs/enums. */ + ////////////////////////////////////////////////////////////////////////// +# if (defined ASSIMP_DOXYGEN_BUILD) +# define C_STRUCT +# define C_ENUM +# else +# define C_STRUCT struct +# define C_ENUM enum +# endif +#endif + +#if (defined(__BORLANDC__) || defined (__BCPLUSPLUS__)) +#error Currently, Borland is unsupported. Feel free to port Assimp. + +// "W8059 Packgröße der Struktur geändert" + +#endif + + + ////////////////////////////////////////////////////////////////////////// + /* Define ASSIMP_BUILD_SINGLETHREADED to compile assimp + * without threading support. The library doesn't utilize + * threads then and is itself not threadsafe. */ + ////////////////////////////////////////////////////////////////////////// +#ifndef ASSIMP_BUILD_SINGLETHREADED +# define ASSIMP_BUILD_SINGLETHREADED +#endif + +#if defined(_DEBUG) || ! defined(NDEBUG) +# define ASSIMP_BUILD_DEBUG +#endif + + ////////////////////////////////////////////////////////////////////////// + /* Define ASSIMP_DOUBLE_PRECISION to compile assimp + * with double precision support (64-bit). */ + ////////////////////////////////////////////////////////////////////////// + +#ifdef ASSIMP_DOUBLE_PRECISION + typedef double ai_real; + typedef signed long long int ai_int; + typedef unsigned long long int ai_uint; +#else // ASSIMP_DOUBLE_PRECISION + typedef float ai_real; + typedef signed int ai_int; + typedef unsigned int ai_uint; +#endif // ASSIMP_DOUBLE_PRECISION + + ////////////////////////////////////////////////////////////////////////// + /* Useful constants */ + ////////////////////////////////////////////////////////////////////////// + +/* This is PI. Hi PI. */ +#define AI_MATH_PI (3.141592653589793238462643383279 ) +#define AI_MATH_TWO_PI (AI_MATH_PI * 2.0) +#define AI_MATH_HALF_PI (AI_MATH_PI * 0.5) + +/* And this is to avoid endless casts to float */ +#define AI_MATH_PI_F (3.1415926538f) +#define AI_MATH_TWO_PI_F (AI_MATH_PI_F * 2.0f) +#define AI_MATH_HALF_PI_F (AI_MATH_PI_F * 0.5f) + +/* Tiny macro to convert from radians to degrees and back */ +#define AI_DEG_TO_RAD(x) ((x)*(ai_real)0.0174532925) +#define AI_RAD_TO_DEG(x) ((x)*(ai_real)57.2957795) + +/* Support for big-endian builds */ +#if defined(__BYTE_ORDER__) +# if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# if !defined(__BIG_ENDIAN__) +# define __BIG_ENDIAN__ +# endif +# else /* little endian */ +# if defined (__BIG_ENDIAN__) +# undef __BIG_ENDIAN__ +# endif +# endif +#endif +#if defined(__BIG_ENDIAN__) +# define AI_BUILD_BIG_ENDIAN +#endif + + +/* To avoid running out of memory + * This can be adjusted for specific use cases + * It's NOT a total limit, just a limit for individual allocations + */ +#define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type)) + +#endif // !! AI_DEFINES_H_INC diff --git a/Assimp/include/assimp/importerdesc.h b/Assimp/include/assimp/importerdesc.h new file mode 100644 index 0000000..6b83b8a --- /dev/null +++ b/Assimp/include/assimp/importerdesc.h @@ -0,0 +1,145 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 importerdesc.h + * @brief #aiImporterFlags, aiImporterDesc implementation. + */ +#pragma once +#ifndef AI_IMPORTER_DESC_H_INC +#define AI_IMPORTER_DESC_H_INC + + +/** Mixed set of flags for #aiImporterDesc, indicating some features + * common to many importers*/ +enum aiImporterFlags +{ + /** Indicates that there is a textual encoding of the + * file format; and that it is supported.*/ + aiImporterFlags_SupportTextFlavour = 0x1, + + /** Indicates that there is a binary encoding of the + * file format; and that it is supported.*/ + aiImporterFlags_SupportBinaryFlavour = 0x2, + + /** Indicates that there is a compressed encoding of the + * file format; and that it is supported.*/ + aiImporterFlags_SupportCompressedFlavour = 0x4, + + /** Indicates that the importer reads only a very particular + * subset of the file format. This happens commonly for + * declarative or procedural formats which cannot easily + * be mapped to #aiScene */ + aiImporterFlags_LimitedSupport = 0x8, + + /** Indicates that the importer is highly experimental and + * should be used with care. This only happens for trunk + * (i.e. SVN) versions, experimental code is not included + * in releases. */ + aiImporterFlags_Experimental = 0x10 +}; + + +/** Meta information about a particular importer. Importers need to fill + * this structure, but they can freely decide how talkative they are. + * A common use case for loader meta info is a user interface + * in which the user can choose between various import/export file + * formats. Building such an UI by hand means a lot of maintenance + * as importers/exporters are added to Assimp, so it might be useful + * to have a common mechanism to query some rough importer + * characteristics. */ +struct aiImporterDesc +{ + /** Full name of the importer (i.e. Blender3D importer)*/ + const char* mName; + + /** Original author (left blank if unknown or whole assimp team) */ + const char* mAuthor; + + /** Current maintainer, left blank if the author maintains */ + const char* mMaintainer; + + /** Implementation comments, i.e. unimplemented features*/ + const char* mComments; + + /** These flags indicate some characteristics common to many + importers. */ + unsigned int mFlags; + + /** Minimum format version that can be loaded im major.minor format, + both are set to 0 if there is either no version scheme + or if the loader doesn't care. */ + unsigned int mMinMajor; + unsigned int mMinMinor; + + /** Maximum format version that can be loaded im major.minor format, + both are set to 0 if there is either no version scheme + or if the loader doesn't care. Loaders that expect to be + forward-compatible to potential future format versions should + indicate zero, otherwise they should specify the current + maximum version.*/ + unsigned int mMaxMajor; + unsigned int mMaxMinor; + + /** List of file extensions this importer can handle. + List entries are separated by space characters. + All entries are lower case without a leading dot (i.e. + "xml dae" would be a valid value. Note that multiple + importers may respond to the same file extension - + assimp calls all importers in the order in which they + are registered and each importer gets the opportunity + to load the file until one importer "claims" the file. Apart + from file extension checks, importers typically use + other methods to quickly reject files (i.e. magic + words) so this does not mean that common or generic + file extensions such as XML would be tediously slow. */ + const char* mFileExtensions; +}; + +/** \brief Returns the Importer description for a given extension. + +Will return a NULL-pointer if no assigned importer desc. was found for the given extension + \param extension [in] The extension to look for + \return A pointer showing to the ImporterDesc, \see aiImporterDesc. +*/ +ASSIMP_API const C_STRUCT aiImporterDesc* aiGetImporterDesc( const char *extension ); + +#endif // AI_IMPORTER_DESC_H_INC diff --git a/Assimp/include/assimp/light.h b/Assimp/include/assimp/light.h new file mode 100644 index 0000000..324339a --- /dev/null +++ b/Assimp/include/assimp/light.h @@ -0,0 +1,258 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 light.h + * @brief Defines the aiLight data structure + */ + +#pragma once +#ifndef AI_LIGHT_H_INC +#define AI_LIGHT_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** Enumerates all supported types of light sources. + */ +enum aiLightSourceType +{ + aiLightSource_UNDEFINED = 0x0, + + //! A directional light source has a well-defined direction + //! but is infinitely far away. That's quite a good + //! approximation for sun light. + aiLightSource_DIRECTIONAL = 0x1, + + //! A point light source has a well-defined position + //! in space but no direction - it emits light in all + //! directions. A normal bulb is a point light. + aiLightSource_POINT = 0x2, + + //! A spot light source emits light in a specific + //! angle. It has a position and a direction it is pointing to. + //! A good example for a spot light is a light spot in + //! sport arenas. + aiLightSource_SPOT = 0x3, + + //! The generic light level of the world, including the bounces + //! of all other light sources. + //! Typically, there's at most one ambient light in a scene. + //! This light type doesn't have a valid position, direction, or + //! other properties, just a color. + aiLightSource_AMBIENT = 0x4, + + //! An area light is a rectangle with predefined size that uniformly + //! emits light from one of its sides. The position is center of the + //! rectangle and direction is its normal vector. + aiLightSource_AREA = 0x5, + + /** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiLightSource_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** Helper structure to describe a light source. + * + * Assimp supports multiple sorts of light sources, including + * directional, point and spot lights. All of them are defined with just + * a single structure and distinguished by their parameters. + * Note - some file formats (such as 3DS, ASE) export a "target point" - + * the point a spot light is looking at (it can even be animated). Assimp + * writes the target point as a subnode of a spotlights's main node, + * called ".Target". However, this is just additional information + * then, the transformation tracks of the main node make the + * spot light already point in the right direction. +*/ +struct aiLight +{ + /** The name of the light source. + * + * There must be a node in the scenegraph with the same name. + * This node specifies the position of the light in the scene + * hierarchy and can be animated. + */ + C_STRUCT aiString mName; + + /** The type of the light source. + * + * aiLightSource_UNDEFINED is not a valid value for this member. + */ + C_ENUM aiLightSourceType mType; + + /** Position of the light source in space. Relative to the + * transformation of the node corresponding to the light. + * + * The position is undefined for directional lights. + */ + C_STRUCT aiVector3D mPosition; + + /** Direction of the light source in space. Relative to the + * transformation of the node corresponding to the light. + * + * The direction is undefined for point lights. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mDirection; + + /** Up direction of the light source in space. Relative to the + * transformation of the node corresponding to the light. + * + * The direction is undefined for point lights. The vector + * may be normalized, but it needn't. + */ + C_STRUCT aiVector3D mUp; + + /** Constant light attenuation factor. + * + * The intensity of the light source at a given distance 'd' from + * the light's position is + * @code + * Atten = 1/( att0 + att1 * d + att2 * d*d) + * @endcode + * This member corresponds to the att0 variable in the equation. + * Naturally undefined for directional lights. + */ + float mAttenuationConstant; + + /** Linear light attenuation factor. + * + * The intensity of the light source at a given distance 'd' from + * the light's position is + * @code + * Atten = 1/( att0 + att1 * d + att2 * d*d) + * @endcode + * This member corresponds to the att1 variable in the equation. + * Naturally undefined for directional lights. + */ + float mAttenuationLinear; + + /** Quadratic light attenuation factor. + * + * The intensity of the light source at a given distance 'd' from + * the light's position is + * @code + * Atten = 1/( att0 + att1 * d + att2 * d*d) + * @endcode + * This member corresponds to the att2 variable in the equation. + * Naturally undefined for directional lights. + */ + float mAttenuationQuadratic; + + /** Diffuse color of the light source + * + * The diffuse light color is multiplied with the diffuse + * material color to obtain the final color that contributes + * to the diffuse shading term. + */ + C_STRUCT aiColor3D mColorDiffuse; + + /** Specular color of the light source + * + * The specular light color is multiplied with the specular + * material color to obtain the final color that contributes + * to the specular shading term. + */ + C_STRUCT aiColor3D mColorSpecular; + + /** Ambient color of the light source + * + * The ambient light color is multiplied with the ambient + * material color to obtain the final color that contributes + * to the ambient shading term. Most renderers will ignore + * this value it, is just a remaining of the fixed-function pipeline + * that is still supported by quite many file formats. + */ + C_STRUCT aiColor3D mColorAmbient; + + /** Inner angle of a spot light's light cone. + * + * The spot light has maximum influence on objects inside this + * angle. The angle is given in radians. It is 2PI for point + * lights and undefined for directional lights. + */ + float mAngleInnerCone; + + /** Outer angle of a spot light's light cone. + * + * The spot light does not affect objects outside this angle. + * The angle is given in radians. It is 2PI for point lights and + * undefined for directional lights. The outer angle must be + * greater than or equal to the inner angle. + * It is assumed that the application uses a smooth + * interpolation between the inner and the outer cone of the + * spot light. + */ + float mAngleOuterCone; + + /** Size of area light source. */ + C_STRUCT aiVector2D mSize; + +#ifdef __cplusplus + + aiLight() + : mType (aiLightSource_UNDEFINED) + , mAttenuationConstant (0.f) + , mAttenuationLinear (1.f) + , mAttenuationQuadratic (0.f) + , mAngleInnerCone ((float)AI_MATH_TWO_PI) + , mAngleOuterCone ((float)AI_MATH_TWO_PI) + , mSize (0.f, 0.f) + { + } + +#endif +}; + +#ifdef __cplusplus +} +#endif + + +#endif // !! AI_LIGHT_H_INC diff --git a/Assimp/include/assimp/material.h b/Assimp/include/assimp/material.h new file mode 100644 index 0000000..a12e7d0 --- /dev/null +++ b/Assimp/include/assimp/material.h @@ -0,0 +1,1564 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 material.h + * @brief Defines the material system of the library + */ +#pragma once +#ifndef AI_MATERIAL_H_INC +#define AI_MATERIAL_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Name for default materials (2nd is used if meshes have UV coords) +#define AI_DEFAULT_MATERIAL_NAME "DefaultMaterial" + +// --------------------------------------------------------------------------- +/** @brief Defines how the Nth texture of a specific type is combined with + * the result of all previous layers. + * + * Example (left: key, right: value):
+ * @code + * DiffColor0 - gray + * DiffTextureOp0 - aiTextureOpMultiply + * DiffTexture0 - tex1.png + * DiffTextureOp0 - aiTextureOpAdd + * DiffTexture1 - tex2.png + * @endcode + * Written as equation, the final diffuse term for a specific pixel would be: + * @code + * diffFinal = DiffColor0 * sampleTex(DiffTexture0,UV0) + + * sampleTex(DiffTexture1,UV0) * diffContrib; + * @endcode + * where 'diffContrib' is the intensity of the incoming light for that pixel. + */ +enum aiTextureOp +{ + /** T = T1 * T2 */ + aiTextureOp_Multiply = 0x0, + + /** T = T1 + T2 */ + aiTextureOp_Add = 0x1, + + /** T = T1 - T2 */ + aiTextureOp_Subtract = 0x2, + + /** T = T1 / T2 */ + aiTextureOp_Divide = 0x3, + + /** T = (T1 + T2) - (T1 * T2) */ + aiTextureOp_SmoothAdd = 0x4, + + /** T = T1 + (T2-0.5) */ + aiTextureOp_SignedAdd = 0x5, + + +#ifndef SWIG + _aiTextureOp_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Defines how UV coordinates outside the [0...1] range are handled. + * + * Commonly referred to as 'wrapping mode'. + */ +enum aiTextureMapMode +{ + /** A texture coordinate u|v is translated to u%1|v%1 + */ + aiTextureMapMode_Wrap = 0x0, + + /** Texture coordinates outside [0...1] + * are clamped to the nearest valid value. + */ + aiTextureMapMode_Clamp = 0x1, + + /** If the texture coordinates for a pixel are outside [0...1] + * the texture is not applied to that pixel + */ + aiTextureMapMode_Decal = 0x3, + + /** A texture coordinate u|v becomes u%1|v%1 if (u-(u%1))%2 is zero and + * 1-(u%1)|1-(v%1) otherwise + */ + aiTextureMapMode_Mirror = 0x2, + +#ifndef SWIG + _aiTextureMapMode_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Defines how the mapping coords for a texture are generated. + * + * Real-time applications typically require full UV coordinates, so the use of + * the aiProcess_GenUVCoords step is highly recommended. It generates proper + * UV channels for non-UV mapped objects, as long as an accurate description + * how the mapping should look like (e.g spherical) is given. + * See the #AI_MATKEY_MAPPING property for more details. + */ +enum aiTextureMapping +{ + /** The mapping coordinates are taken from an UV channel. + * + * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * the texture coordinates are to be taken from (remember, + * meshes can have more than one UV channel). + */ + aiTextureMapping_UV = 0x0, + + /** Spherical mapping */ + aiTextureMapping_SPHERE = 0x1, + + /** Cylindrical mapping */ + aiTextureMapping_CYLINDER = 0x2, + + /** Cubic mapping */ + aiTextureMapping_BOX = 0x3, + + /** Planar mapping */ + aiTextureMapping_PLANE = 0x4, + + /** Undefined mapping. Have fun. */ + aiTextureMapping_OTHER = 0x5, + + +#ifndef SWIG + _aiTextureMapping_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Defines the purpose of a texture + * + * This is a very difficult topic. Different 3D packages support different + * kinds of textures. For very common texture types, such as bumpmaps, the + * rendering results depend on implementation details in the rendering + * pipelines of these applications. Assimp loads all texture references from + * the model file and tries to determine which of the predefined texture + * types below is the best choice to match the original use of the texture + * as closely as possible.
+ * + * In content pipelines you'll usually define how textures have to be handled, + * and the artists working on models have to conform to this specification, + * regardless which 3D tool they're using. + */ +enum aiTextureType +{ + /** Dummy value. + * + * No texture, but the value to be used as 'texture semantic' + * (#aiMaterialProperty::mSemantic) for all material properties + * *not* related to textures. + */ + aiTextureType_NONE = 0x0, + + + + /** The texture is combined with the result of the diffuse + * lighting equation. + */ + aiTextureType_DIFFUSE = 0x1, + + /** The texture is combined with the result of the specular + * lighting equation. + */ + aiTextureType_SPECULAR = 0x2, + + /** The texture is combined with the result of the ambient + * lighting equation. + */ + aiTextureType_AMBIENT = 0x3, + + /** The texture is added to the result of the lighting + * calculation. It isn't influenced by incoming light. + */ + aiTextureType_EMISSIVE = 0x4, + + /** The texture is a height map. + * + * By convention, higher gray-scale values stand for + * higher elevations from the base height. + */ + aiTextureType_HEIGHT = 0x5, + + /** The texture is a (tangent space) normal-map. + * + * Again, there are several conventions for tangent-space + * normal maps. Assimp does (intentionally) not + * distinguish here. + */ + aiTextureType_NORMALS = 0x6, + + /** The texture defines the glossiness of the material. + * + * The glossiness is in fact the exponent of the specular + * (phong) lighting equation. Usually there is a conversion + * function defined to map the linear color values in the + * texture to a suitable exponent. Have fun. + */ + aiTextureType_SHININESS = 0x7, + + /** The texture defines per-pixel opacity. + * + * Usually 'white' means opaque and 'black' means + * 'transparency'. Or quite the opposite. Have fun. + */ + aiTextureType_OPACITY = 0x8, + + /** Displacement texture + * + * The exact purpose and format is application-dependent. + * Higher color values stand for higher vertex displacements. + */ + aiTextureType_DISPLACEMENT = 0x9, + + /** Lightmap texture (aka Ambient Occlusion) + * + * Both 'Lightmaps' and dedicated 'ambient occlusion maps' are + * covered by this material property. The texture contains a + * scaling value for the final color value of a pixel. Its + * intensity is not affected by incoming light. + */ + aiTextureType_LIGHTMAP = 0xA, + + /** Reflection texture + * + * Contains the color of a perfect mirror reflection. + * Rarely used, almost never for real-time applications. + */ + aiTextureType_REFLECTION = 0xB, + + /** Unknown texture + * + * A texture reference that does not match any of the definitions + * above is considered to be 'unknown'. It is still imported, + * but is excluded from any further postprocessing. + */ + aiTextureType_UNKNOWN = 0xC, + + +#ifndef SWIG + _aiTextureType_Force32Bit = INT_MAX +#endif +}; + +#define AI_TEXTURE_TYPE_MAX aiTextureType_UNKNOWN + +// --------------------------------------------------------------------------- +/** @brief Defines all shading models supported by the library + * + * The list of shading modes has been taken from Blender. + * See Blender documentation for more information. The API does + * not distinguish between "specular" and "diffuse" shaders (thus the + * specular term for diffuse shading models like Oren-Nayar remains + * undefined).
+ * Again, this value is just a hint. Assimp tries to select the shader whose + * most common implementation matches the original rendering results of the + * 3D modeller which wrote a particular model as closely as possible. + */ +enum aiShadingMode +{ + /** Flat shading. Shading is done on per-face base, + * diffuse only. Also known as 'faceted shading'. + */ + aiShadingMode_Flat = 0x1, + + /** Simple Gouraud shading. + */ + aiShadingMode_Gouraud = 0x2, + + /** Phong-Shading - + */ + aiShadingMode_Phong = 0x3, + + /** Phong-Blinn-Shading + */ + aiShadingMode_Blinn = 0x4, + + /** Toon-Shading per pixel + * + * Also known as 'comic' shader. + */ + aiShadingMode_Toon = 0x5, + + /** OrenNayar-Shading per pixel + * + * Extension to standard Lambertian shading, taking the + * roughness of the material into account + */ + aiShadingMode_OrenNayar = 0x6, + + /** Minnaert-Shading per pixel + * + * Extension to standard Lambertian shading, taking the + * "darkness" of the material into account + */ + aiShadingMode_Minnaert = 0x7, + + /** CookTorrance-Shading per pixel + * + * Special shader for metallic surfaces. + */ + aiShadingMode_CookTorrance = 0x8, + + /** No shading at all. Constant light influence of 1.0. + */ + aiShadingMode_NoShading = 0x9, + + /** Fresnel shading + */ + aiShadingMode_Fresnel = 0xa, + + +#ifndef SWIG + _aiShadingMode_Force32Bit = INT_MAX +#endif +}; + + +// --------------------------------------------------------------------------- +/** @brief Defines some mixed flags for a particular texture. + * + * Usually you'll instruct your cg artists how textures have to look like ... + * and how they will be processed in your application. However, if you use + * Assimp for completely generic loading purposes you might also need to + * process these flags in order to display as many 'unknown' 3D models as + * possible correctly. + * + * This corresponds to the #AI_MATKEY_TEXFLAGS property. +*/ +enum aiTextureFlags +{ + /** The texture's color values have to be inverted (componentwise 1-n) + */ + aiTextureFlags_Invert = 0x1, + + /** Explicit request to the application to process the alpha channel + * of the texture. + * + * Mutually exclusive with #aiTextureFlags_IgnoreAlpha. These + * flags are set if the library can say for sure that the alpha + * channel is used/is not used. If the model format does not + * define this, it is left to the application to decide whether + * the texture alpha channel - if any - is evaluated or not. + */ + aiTextureFlags_UseAlpha = 0x2, + + /** Explicit request to the application to ignore the alpha channel + * of the texture. + * + * Mutually exclusive with #aiTextureFlags_UseAlpha. + */ + aiTextureFlags_IgnoreAlpha = 0x4, + +#ifndef SWIG + _aiTextureFlags_Force32Bit = INT_MAX +#endif +}; + + +// --------------------------------------------------------------------------- +/** @brief Defines alpha-blend flags. + * + * If you're familiar with OpenGL or D3D, these flags aren't new to you. + * They define *how* the final color value of a pixel is computed, basing + * on the previous color at that pixel and the new color value from the + * material. + * The blend formula is: + * @code + * SourceColor * SourceBlend + DestColor * DestBlend + * @endcode + * where DestColor is the previous color in the framebuffer at this + * position and SourceColor is the material color before the transparency + * calculation.
+ * This corresponds to the #AI_MATKEY_BLEND_FUNC property. +*/ +enum aiBlendMode +{ + /** + * Formula: + * @code + * SourceColor*SourceAlpha + DestColor*(1-SourceAlpha) + * @endcode + */ + aiBlendMode_Default = 0x0, + + /** Additive blending + * + * Formula: + * @code + * SourceColor*1 + DestColor*1 + * @endcode + */ + aiBlendMode_Additive = 0x1, + + // we don't need more for the moment, but we might need them + // in future versions ... + +#ifndef SWIG + _aiBlendMode_Force32Bit = INT_MAX +#endif +}; + + +#include "./Compiler/pushpack1.h" + +// --------------------------------------------------------------------------- +/** @brief Defines how an UV channel is transformed. + * + * This is just a helper structure for the #AI_MATKEY_UVTRANSFORM key. + * See its documentation for more details. + * + * Typically you'll want to build a matrix of this information. However, + * we keep separate scaling/translation/rotation values to make it + * easier to process and optimize UV transformations internally. + */ +struct aiUVTransform +{ + /** Translation on the u and v axes. + * + * The default value is (0|0). + */ + C_STRUCT aiVector2D mTranslation; + + /** Scaling on the u and v axes. + * + * The default value is (1|1). + */ + C_STRUCT aiVector2D mScaling; + + /** Rotation - in counter-clockwise direction. + * + * The rotation angle is specified in radians. The + * rotation center is 0.5f|0.5f. The default value + * 0.f. + */ + ai_real mRotation; + + +#ifdef __cplusplus + aiUVTransform() + : mTranslation (0.0,0.0) + , mScaling (1.0,1.0) + , mRotation (0.0) + { + // nothing to be done here ... + } +#endif + +} PACK_STRUCT; + +#include "./Compiler/poppack1.h" + +//! @cond AI_DOX_INCLUDE_INTERNAL +// --------------------------------------------------------------------------- +/** @brief A very primitive RTTI system for the contents of material + * properties. + */ +enum aiPropertyTypeInfo +{ + /** Array of single-precision (32 Bit) floats + * + * It is possible to use aiGetMaterialInteger[Array]() (or the C++-API + * aiMaterial::Get()) to query properties stored in floating-point format. + * The material system performs the type conversion automatically. + */ + aiPTI_Float = 0x1, + + /** Array of double-precision (64 Bit) floats + * + * It is possible to use aiGetMaterialInteger[Array]() (or the C++-API + * aiMaterial::Get()) to query properties stored in floating-point format. + * The material system performs the type conversion automatically. + */ + aiPTI_Double = 0x2, + + /** The material property is an aiString. + * + * Arrays of strings aren't possible, aiGetMaterialString() (or the + * C++-API aiMaterial::Get()) *must* be used to query a string property. + */ + aiPTI_String = 0x3, + + /** Array of (32 Bit) integers + * + * It is possible to use aiGetMaterialFloat[Array]() (or the C++-API + * aiMaterial::Get()) to query properties stored in integer format. + * The material system performs the type conversion automatically. + */ + aiPTI_Integer = 0x4, + + + /** Simple binary buffer, content undefined. Not convertible to anything. + */ + aiPTI_Buffer = 0x5, + + + /** This value is not used. It is just there to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiPTI_Force32Bit = INT_MAX +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Data structure for a single material property + * + * As an user, you'll probably never need to deal with this data structure. + * Just use the provided aiGetMaterialXXX() or aiMaterial::Get() family + * of functions to query material properties easily. Processing them + * manually is faster, but it is not the recommended way. It isn't worth + * the effort.
+ * Material property names follow a simple scheme: + * @code + * $ + * ? + * A public property, there must be corresponding AI_MATKEY_XXX define + * 2nd: Public, but ignored by the #aiProcess_RemoveRedundantMaterials + * post-processing step. + * ~ + * A temporary property for internal use. + * @endcode + * @see aiMaterial + */ +struct aiMaterialProperty +{ + /** Specifies the name of the property (key) + * Keys are generally case insensitive. + */ + C_STRUCT aiString mKey; + + /** Textures: Specifies their exact usage semantic. + * For non-texture properties, this member is always 0 + * (or, better-said, #aiTextureType_NONE). + */ + unsigned int mSemantic; + + /** Textures: Specifies the index of the texture. + * For non-texture properties, this member is always 0. + */ + unsigned int mIndex; + + /** Size of the buffer mData is pointing to, in bytes. + * This value may not be 0. + */ + unsigned int mDataLength; + + /** Type information for the property. + * + * Defines the data layout inside the data buffer. This is used + * by the library internally to perform debug checks and to + * utilize proper type conversions. + * (It's probably a hacky solution, but it works.) + */ + C_ENUM aiPropertyTypeInfo mType; + + /** Binary buffer to hold the property's value. + * The size of the buffer is always mDataLength. + */ + char* mData; + +#ifdef __cplusplus + + aiMaterialProperty() + : mSemantic( 0 ) + , mIndex( 0 ) + , mDataLength( 0 ) + , mType( aiPTI_Float ) + , mData( NULL ) + { + } + + ~aiMaterialProperty() { + delete[] mData; + } + +#endif +}; +//! @endcond + +#ifdef __cplusplus +} // We need to leave the "C" block here to allow template member functions +#endif + +// --------------------------------------------------------------------------- +/** @brief Data structure for a material +* +* Material data is stored using a key-value structure. A single key-value +* pair is called a 'material property'. C++ users should use the provided +* member functions of aiMaterial to process material properties, C users +* have to stick with the aiMaterialGetXXX family of unbound functions. +* The library defines a set of standard keys (AI_MATKEY_XXX). +*/ +#ifdef __cplusplus +struct ASSIMP_API aiMaterial +#else +struct aiMaterial +#endif +{ + +#ifdef __cplusplus + +public: + + aiMaterial(); + ~aiMaterial(); + + // ------------------------------------------------------------------- + /** @brief Retrieve an array of Type values with a specific key + * from the material + * + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param type .. set by AI_MATKEY_XXX + * @param idx .. set by AI_MATKEY_XXX + * @param pOut Pointer to a buffer to receive the result. + * @param pMax Specifies the size of the given buffer, in Type's. + * Receives the number of values (not bytes!) read. + * NULL is a valid value for this parameter. + */ + template + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, Type* pOut, unsigned int* pMax) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, int* pOut, unsigned int* pMax) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, ai_real* pOut, unsigned int* pMax) const; + + // ------------------------------------------------------------------- + /** @brief Retrieve a Type value with a specific key + * from the material + * + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param type Specifies the type of the texture to be retrieved ( + * e.g. diffuse, specular, height map ...) + * @param idx Index of the texture to be retrieved. + * @param pOut Reference to receive the output value + */ + template + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx,Type& pOut) const; + + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, int& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, ai_real& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiString& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiColor3D& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiColor4D& pOut) const; + + aiReturn Get(const char* pKey,unsigned int type, + unsigned int idx, aiUVTransform& pOut) const; + + // ------------------------------------------------------------------- + /** Get the number of textures for a particular texture type. + * @param type Texture type to check for + * @return Number of textures for this type. + * @note A texture can be easily queried using #GetTexture() */ + unsigned int GetTextureCount(aiTextureType type) const; + + // ------------------------------------------------------------------- + /** Helper function to get all parameters pertaining to a + * particular texture slot from a material. + * + * This function is provided just for convenience, you could also + * read the single material properties manually. + * @param type Specifies the type of the texture to be retrieved ( + * e.g. diffuse, specular, height map ...) + * @param index Index of the texture to be retrieved. The function fails + * if there is no texture of that type with this index. + * #GetTextureCount() can be used to determine the number of textures + * per texture type. + * @param path Receives the path to the texture. + * If the texture is embedded, receives a '*' followed by the id of + * the texture (for the textures stored in the corresponding scene) which + * can be converted to an int using a function like atoi. + * NULL is a valid value. + * @param mapping The texture mapping. + * NULL is allowed as value. + * @param uvindex Receives the UV index of the texture. + * NULL is a valid value. + * @param blend Receives the blend factor for the texture + * NULL is a valid value. + * @param op Receives the texture operation to be performed between + * this texture and the previous texture. NULL is allowed as value. + * @param mapmode Receives the mapping modes to be used for the texture. + * The parameter may be NULL but if it is a valid pointer it MUST + * point to an array of 3 aiTextureMapMode's (one for each + * axis: UVW order (=XYZ)). + */ + // ------------------------------------------------------------------- + aiReturn GetTexture(aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + aiTextureMapping* mapping = NULL, + unsigned int* uvindex = NULL, + ai_real* blend = NULL, + aiTextureOp* op = NULL, + aiTextureMapMode* mapmode = NULL) const; + + + // Setters + + + // ------------------------------------------------------------------------------ + /** @brief Add a property with a given key and type info to the material + * structure + * + * @param pInput Pointer to input data + * @param pSizeInBytes Size of input data + * @param pKey Key/Usage of the property (AI_MATKEY_XXX) + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro + * @param pType Type information hint */ + aiReturn AddBinaryProperty (const void* pInput, + unsigned int pSizeInBytes, + const char* pKey, + unsigned int type , + unsigned int index , + aiPropertyTypeInfo pType); + + // ------------------------------------------------------------------------------ + /** @brief Add a string property with a given key and type info to the + * material structure + * + * @param pInput Input string + * @param pKey Key/Usage of the property (AI_MATKEY_XXX) + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ + aiReturn AddProperty (const aiString* pInput, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + // ------------------------------------------------------------------------------ + /** @brief Add a property with a given key to the material structure + * @param pInput Pointer to the input data + * @param pNumValues Number of values in the array + * @param pKey Key/Usage of the property (AI_MATKEY_XXX) + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ + template + aiReturn AddProperty (const TYPE* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiVector3D* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiColor3D* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiColor4D* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const int* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const float* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const double* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + aiReturn AddProperty (const aiUVTransform* pInput, + unsigned int pNumValues, + const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + // ------------------------------------------------------------------------------ + /** @brief Remove a given key from the list. + * + * The function fails if the key isn't found + * @param pKey Key to be deleted + * @param type Set by the AI_MATKEY_XXX macro + * @param index Set by the AI_MATKEY_XXX macro */ + aiReturn RemoveProperty (const char* pKey, + unsigned int type = 0, + unsigned int index = 0); + + // ------------------------------------------------------------------------------ + /** @brief Removes all properties from the material. + * + * The data array remains allocated so adding new properties is quite fast. */ + void Clear(); + + // ------------------------------------------------------------------------------ + /** Copy the property list of a material + * @param pcDest Destination material + * @param pcSrc Source material + */ + static void CopyPropertyList(aiMaterial* pcDest, + const aiMaterial* pcSrc); + + +#endif + + /** List of all material properties loaded. */ + C_STRUCT aiMaterialProperty** mProperties; + + /** Number of properties in the data base */ + unsigned int mNumProperties; + + /** Storage allocated */ + unsigned int mNumAllocated; +}; + +// Go back to extern "C" again +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +#define AI_MATKEY_NAME "?mat.name",0,0 +#define AI_MATKEY_TWOSIDED "$mat.twosided",0,0 +#define AI_MATKEY_SHADING_MODEL "$mat.shadingm",0,0 +#define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,0 +#define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0 +#define AI_MATKEY_OPACITY "$mat.opacity",0,0 +#define AI_MATKEY_BUMPSCALING "$mat.bumpscaling",0,0 +#define AI_MATKEY_SHININESS "$mat.shininess",0,0 +#define AI_MATKEY_REFLECTIVITY "$mat.reflectivity",0,0 +#define AI_MATKEY_SHININESS_STRENGTH "$mat.shinpercent",0,0 +#define AI_MATKEY_REFRACTI "$mat.refracti",0,0 +#define AI_MATKEY_COLOR_DIFFUSE "$clr.diffuse",0,0 +#define AI_MATKEY_COLOR_AMBIENT "$clr.ambient",0,0 +#define AI_MATKEY_COLOR_SPECULAR "$clr.specular",0,0 +#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive",0,0 +#define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0 +#define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0 +#define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "?bg.global",0,0 + +// --------------------------------------------------------------------------- +// Pure key names for all texture-related properties +//! @cond MATS_DOC_FULL +#define _AI_MATKEY_TEXTURE_BASE "$tex.file" +#define _AI_MATKEY_UVWSRC_BASE "$tex.uvwsrc" +#define _AI_MATKEY_TEXOP_BASE "$tex.op" +#define _AI_MATKEY_MAPPING_BASE "$tex.mapping" +#define _AI_MATKEY_TEXBLEND_BASE "$tex.blend" +#define _AI_MATKEY_MAPPINGMODE_U_BASE "$tex.mapmodeu" +#define _AI_MATKEY_MAPPINGMODE_V_BASE "$tex.mapmodev" +#define _AI_MATKEY_TEXMAP_AXIS_BASE "$tex.mapaxis" +#define _AI_MATKEY_UVTRANSFORM_BASE "$tex.uvtrafo" +#define _AI_MATKEY_TEXFLAGS_BASE "$tex.flags" +//! @endcond + +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXTURE(type, N) _AI_MATKEY_TEXTURE_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXTURE_DIFFUSE(N) \ + AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXTURE_SPECULAR(N) \ + AI_MATKEY_TEXTURE(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXTURE_AMBIENT(N) \ + AI_MATKEY_TEXTURE(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXTURE_EMISSIVE(N) \ + AI_MATKEY_TEXTURE(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXTURE_NORMALS(N) \ + AI_MATKEY_TEXTURE(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXTURE_HEIGHT(N) \ + AI_MATKEY_TEXTURE(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXTURE_SHININESS(N) \ + AI_MATKEY_TEXTURE(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXTURE_OPACITY(N) \ + AI_MATKEY_TEXTURE(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXTURE_DISPLACEMENT(N) \ + AI_MATKEY_TEXTURE(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXTURE_LIGHTMAP(N) \ + AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXTURE_REFLECTION(N) \ + AI_MATKEY_TEXTURE(aiTextureType_REFLECTION,N) + +//! @endcond + +// --------------------------------------------------------------------------- +#define AI_MATKEY_UVWSRC(type, N) _AI_MATKEY_UVWSRC_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_UVWSRC_DIFFUSE(N) \ + AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_UVWSRC_SPECULAR(N) \ + AI_MATKEY_UVWSRC(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_UVWSRC_AMBIENT(N) \ + AI_MATKEY_UVWSRC(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_UVWSRC_EMISSIVE(N) \ + AI_MATKEY_UVWSRC(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_UVWSRC_NORMALS(N) \ + AI_MATKEY_UVWSRC(aiTextureType_NORMALS,N) + +#define AI_MATKEY_UVWSRC_HEIGHT(N) \ + AI_MATKEY_UVWSRC(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_UVWSRC_SHININESS(N) \ + AI_MATKEY_UVWSRC(aiTextureType_SHININESS,N) + +#define AI_MATKEY_UVWSRC_OPACITY(N) \ + AI_MATKEY_UVWSRC(aiTextureType_OPACITY,N) + +#define AI_MATKEY_UVWSRC_DISPLACEMENT(N) \ + AI_MATKEY_UVWSRC(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_UVWSRC_LIGHTMAP(N) \ + AI_MATKEY_UVWSRC(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_UVWSRC_REFLECTION(N) \ + AI_MATKEY_UVWSRC(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXOP(type, N) _AI_MATKEY_TEXOP_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXOP_DIFFUSE(N) \ + AI_MATKEY_TEXOP(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXOP_SPECULAR(N) \ + AI_MATKEY_TEXOP(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXOP_AMBIENT(N) \ + AI_MATKEY_TEXOP(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXOP_EMISSIVE(N) \ + AI_MATKEY_TEXOP(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXOP_NORMALS(N) \ + AI_MATKEY_TEXOP(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXOP_HEIGHT(N) \ + AI_MATKEY_TEXOP(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXOP_SHININESS(N) \ + AI_MATKEY_TEXOP(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXOP_OPACITY(N) \ + AI_MATKEY_TEXOP(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXOP_DISPLACEMENT(N) \ + AI_MATKEY_TEXOP(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXOP_LIGHTMAP(N) \ + AI_MATKEY_TEXOP(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXOP_REFLECTION(N) \ + AI_MATKEY_TEXOP(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_MAPPING(type, N) _AI_MATKEY_MAPPING_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_MAPPING_DIFFUSE(N) \ + AI_MATKEY_MAPPING(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_MAPPING_SPECULAR(N) \ + AI_MATKEY_MAPPING(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_MAPPING_AMBIENT(N) \ + AI_MATKEY_MAPPING(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_MAPPING_EMISSIVE(N) \ + AI_MATKEY_MAPPING(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_MAPPING_NORMALS(N) \ + AI_MATKEY_MAPPING(aiTextureType_NORMALS,N) + +#define AI_MATKEY_MAPPING_HEIGHT(N) \ + AI_MATKEY_MAPPING(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_MAPPING_SHININESS(N) \ + AI_MATKEY_MAPPING(aiTextureType_SHININESS,N) + +#define AI_MATKEY_MAPPING_OPACITY(N) \ + AI_MATKEY_MAPPING(aiTextureType_OPACITY,N) + +#define AI_MATKEY_MAPPING_DISPLACEMENT(N) \ + AI_MATKEY_MAPPING(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_MAPPING_LIGHTMAP(N) \ + AI_MATKEY_MAPPING(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_MAPPING_REFLECTION(N) \ + AI_MATKEY_MAPPING(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXBLEND(type, N) _AI_MATKEY_TEXBLEND_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXBLEND_DIFFUSE(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXBLEND_SPECULAR(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXBLEND_AMBIENT(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXBLEND_EMISSIVE(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXBLEND_NORMALS(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXBLEND_HEIGHT(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXBLEND_SHININESS(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXBLEND_OPACITY(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXBLEND_DISPLACEMENT(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXBLEND_LIGHTMAP(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXBLEND_REFLECTION(N) \ + AI_MATKEY_TEXBLEND(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_MAPPINGMODE_U(type, N) _AI_MATKEY_MAPPINGMODE_U_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_MAPPINGMODE_U_DIFFUSE(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_MAPPINGMODE_U_SPECULAR(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_MAPPINGMODE_U_AMBIENT(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_MAPPINGMODE_U_EMISSIVE(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_MAPPINGMODE_U_NORMALS(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_NORMALS,N) + +#define AI_MATKEY_MAPPINGMODE_U_HEIGHT(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_MAPPINGMODE_U_SHININESS(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_SHININESS,N) + +#define AI_MATKEY_MAPPINGMODE_U_OPACITY(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_OPACITY,N) + +#define AI_MATKEY_MAPPINGMODE_U_DISPLACEMENT(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_MAPPINGMODE_U_REFLECTION(N) \ + AI_MATKEY_MAPPINGMODE_U(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_MAPPINGMODE_V(type, N) _AI_MATKEY_MAPPINGMODE_V_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_MAPPINGMODE_V_DIFFUSE(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_MAPPINGMODE_V_SPECULAR(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_MAPPINGMODE_V_AMBIENT(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_MAPPINGMODE_V_EMISSIVE(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_MAPPINGMODE_V_NORMALS(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_NORMALS,N) + +#define AI_MATKEY_MAPPINGMODE_V_HEIGHT(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_MAPPINGMODE_V_SHININESS(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_SHININESS,N) + +#define AI_MATKEY_MAPPINGMODE_V_OPACITY(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_OPACITY,N) + +#define AI_MATKEY_MAPPINGMODE_V_DISPLACEMENT(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_MAPPINGMODE_V_REFLECTION(N) \ + AI_MATKEY_MAPPINGMODE_V(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXMAP_AXIS(type, N) _AI_MATKEY_TEXMAP_AXIS_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXMAP_AXIS_DIFFUSE(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXMAP_AXIS_SPECULAR(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXMAP_AXIS_AMBIENT(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXMAP_AXIS_EMISSIVE(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXMAP_AXIS_NORMALS(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXMAP_AXIS_HEIGHT(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXMAP_AXIS_SHININESS(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXMAP_AXIS_OPACITY(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXMAP_AXIS_DISPLACEMENT(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXMAP_AXIS_LIGHTMAP(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXMAP_AXIS_REFLECTION(N) \ + AI_MATKEY_TEXMAP_AXIS(aiTextureType_REFLECTION,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_UVTRANSFORM(type, N) _AI_MATKEY_UVTRANSFORM_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_UVTRANSFORM_DIFFUSE(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_UVTRANSFORM_SPECULAR(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_UVTRANSFORM_AMBIENT(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_UVTRANSFORM_EMISSIVE(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_UVTRANSFORM_NORMALS(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_NORMALS,N) + +#define AI_MATKEY_UVTRANSFORM_HEIGHT(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_UVTRANSFORM_SHININESS(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_SHININESS,N) + +#define AI_MATKEY_UVTRANSFORM_OPACITY(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_OPACITY,N) + +#define AI_MATKEY_UVTRANSFORM_DISPLACEMENT(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_UVTRANSFORM_LIGHTMAP(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_UVTRANSFORM_REFLECTION(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_REFLECTION,N) + +#define AI_MATKEY_UVTRANSFORM_UNKNOWN(N) \ + AI_MATKEY_UVTRANSFORM(aiTextureType_UNKNOWN,N) + +//! @endcond +// --------------------------------------------------------------------------- +#define AI_MATKEY_TEXFLAGS(type, N) _AI_MATKEY_TEXFLAGS_BASE,type,N + +// For backward compatibility and simplicity +//! @cond MATS_DOC_FULL +#define AI_MATKEY_TEXFLAGS_DIFFUSE(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_DIFFUSE,N) + +#define AI_MATKEY_TEXFLAGS_SPECULAR(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_SPECULAR,N) + +#define AI_MATKEY_TEXFLAGS_AMBIENT(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_AMBIENT,N) + +#define AI_MATKEY_TEXFLAGS_EMISSIVE(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_EMISSIVE,N) + +#define AI_MATKEY_TEXFLAGS_NORMALS(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_NORMALS,N) + +#define AI_MATKEY_TEXFLAGS_HEIGHT(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_HEIGHT,N) + +#define AI_MATKEY_TEXFLAGS_SHININESS(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_SHININESS,N) + +#define AI_MATKEY_TEXFLAGS_OPACITY(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_OPACITY,N) + +#define AI_MATKEY_TEXFLAGS_DISPLACEMENT(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_DISPLACEMENT,N) + +#define AI_MATKEY_TEXFLAGS_LIGHTMAP(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_LIGHTMAP,N) + +#define AI_MATKEY_TEXFLAGS_REFLECTION(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_REFLECTION,N) + +#define AI_MATKEY_TEXFLAGS_UNKNOWN(N) \ + AI_MATKEY_TEXFLAGS(aiTextureType_UNKNOWN,N) + +//! @endcond +//! +// --------------------------------------------------------------------------- +/** @brief Retrieve a material property with a specific key from the material + * + * @param pMat Pointer to the input material. May not be NULL + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param type Specifies the type of the texture to be retrieved ( + * e.g. diffuse, specular, height map ...) + * @param index Index of the texture to be retrieved. + * @param pPropOut Pointer to receive a pointer to a valid aiMaterialProperty + * structure or NULL if the key has not been found. */ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialProperty( + const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + const C_STRUCT aiMaterialProperty** pPropOut); + +// --------------------------------------------------------------------------- +/** @brief Retrieve an array of float values with a specific key + * from the material + * + * Pass one of the AI_MATKEY_XXX constants for the last three parameters (the + * example reads the #AI_MATKEY_UVTRANSFORM property of the first diffuse texture) + * @code + * aiUVTransform trafo; + * unsigned int max = sizeof(aiUVTransform); + * if (AI_SUCCESS != aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE,0), + * (float*)&trafo, &max) || sizeof(aiUVTransform) != max) + * { + * // error handling + * } + * @endcode + * + * @param pMat Pointer to the input material. May not be NULL + * @param pKey Key to search for. One of the AI_MATKEY_XXX constants. + * @param pOut Pointer to a buffer to receive the result. + * @param pMax Specifies the size of the given buffer, in float's. + * Receives the number of values (not bytes!) read. + * @param type (see the code sample above) + * @param index (see the code sample above) + * @return Specifies whether the key has been found. If not, the output + * arrays remains unmodified and pMax is set to 0.*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( + const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + ai_real* pOut, + unsigned int* pMax); + + +#ifdef __cplusplus + +// --------------------------------------------------------------------------- +/** @brief Retrieve a single float property with a specific key from the material. +* +* Pass one of the AI_MATKEY_XXX constants for the last three parameters (the +* example reads the #AI_MATKEY_SHININESS_STRENGTH property of the first diffuse texture) +* @code +* float specStrength = 1.f; // default value, remains unmodified if we fail. +* aiGetMaterialFloat(mat, AI_MATKEY_SHININESS_STRENGTH, +* (float*)&specStrength); +* @endcode +* +* @param pMat Pointer to the input material. May not be NULL +* @param pKey Key to search for. One of the AI_MATKEY_XXX constants. +* @param pOut Receives the output float. +* @param type (see the code sample above) +* @param index (see the code sample above) +* @return Specifies whether the key has been found. If not, the output +* float remains unmodified.*/ +// --------------------------------------------------------------------------- +inline aiReturn aiGetMaterialFloat(const aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + ai_real* pOut) +{ + return aiGetMaterialFloatArray(pMat,pKey,type,index,pOut,(unsigned int*)0x0); +} + +#else + +// Use our friend, the C preprocessor +#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \ + aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL) + +#endif //!__cplusplus + + +// --------------------------------------------------------------------------- +/** @brief Retrieve an array of integer values with a specific key + * from a material + * + * See the sample for aiGetMaterialFloatArray for more information.*/ +ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + int* pOut, + unsigned int* pMax); + + +#ifdef __cplusplus + +// --------------------------------------------------------------------------- +/** @brief Retrieve an integer property with a specific key from a material + * + * See the sample for aiGetMaterialFloat for more information.*/ +// --------------------------------------------------------------------------- +inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + int* pOut) +{ + return aiGetMaterialIntegerArray(pMat,pKey,type,index,pOut,(unsigned int*)0x0); +} + +#else + +// use our friend, the C preprocessor +#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \ + aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL) + +#endif //!__cplusplus + + + +// --------------------------------------------------------------------------- +/** @brief Retrieve a color value from the material property table +* +* See the sample for aiGetMaterialFloat for more information*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + C_STRUCT aiColor4D* pOut); + + +// --------------------------------------------------------------------------- +/** @brief Retrieve a aiUVTransform value from the material property table +* +* See the sample for aiGetMaterialFloat for more information*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialUVTransform(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + C_STRUCT aiUVTransform* pOut); + + +// --------------------------------------------------------------------------- +/** @brief Retrieve a string from the material property table +* +* See the sample for aiGetMaterialFloat for more information.*/ +// --------------------------------------------------------------------------- +ASSIMP_API C_ENUM aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat, + const char* pKey, + unsigned int type, + unsigned int index, + C_STRUCT aiString* pOut); + +// --------------------------------------------------------------------------- +/** Get the number of textures for a particular texture type. + * @param[in] pMat Pointer to the input material. May not be NULL + * @param type Texture type to check for + * @return Number of textures for this type. + * @note A texture can be easily queried using #aiGetMaterialTexture() */ +// --------------------------------------------------------------------------- +ASSIMP_API unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat, + C_ENUM aiTextureType type); + +// --------------------------------------------------------------------------- +/** @brief Helper function to get all values pertaining to a particular + * texture slot from a material structure. + * + * This function is provided just for convenience. You could also read the + * texture by parsing all of its properties manually. This function bundles + * all of them in a huge function monster. + * + * @param[in] mat Pointer to the input material. May not be NULL + * @param[in] type Specifies the texture stack to read from (e.g. diffuse, + * specular, height map ...). + * @param[in] index Index of the texture. The function fails if the + * requested index is not available for this texture type. + * #aiGetMaterialTextureCount() can be used to determine the number of + * textures in a particular texture stack. + * @param[out] path Receives the output path + * If the texture is embedded, receives a '*' followed by the id of + * the texture (for the textures stored in the corresponding scene) which + * can be converted to an int using a function like atoi. + * This parameter must be non-null. + * @param mapping The texture mapping mode to be used. + * Pass NULL if you're not interested in this information. + * @param[out] uvindex For UV-mapped textures: receives the index of the UV + * source channel. Unmodified otherwise. + * Pass NULL if you're not interested in this information. + * @param[out] blend Receives the blend factor for the texture + * Pass NULL if you're not interested in this information. + * @param[out] op Receives the texture blend operation to be perform between + * this texture and the previous texture. + * Pass NULL if you're not interested in this information. + * @param[out] mapmode Receives the mapping modes to be used for the texture. + * Pass NULL if you're not interested in this information. Otherwise, + * pass a pointer to an array of two aiTextureMapMode's (one for each + * axis, UV order). + * @param[out] flags Receives the the texture flags. + * @return AI_SUCCESS on success, otherwise something else. Have fun.*/ +// --------------------------------------------------------------------------- +#ifdef __cplusplus +ASSIMP_API aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, + aiTextureType type, + unsigned int index, + aiString* path, + aiTextureMapping* mapping = NULL, + unsigned int* uvindex = NULL, + ai_real* blend = NULL, + aiTextureOp* op = NULL, + aiTextureMapMode* mapmode = NULL, + unsigned int* flags = NULL); +#else +C_ENUM aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, + C_ENUM aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + C_ENUM aiTextureMapping* mapping /*= NULL*/, + unsigned int* uvindex /*= NULL*/, + ai_real* blend /*= NULL*/, + C_ENUM aiTextureOp* op /*= NULL*/, + C_ENUM aiTextureMapMode* mapmode /*= NULL*/, + unsigned int* flags /*= NULL*/); +#endif // !#ifdef __cplusplus + +#ifdef __cplusplus +} + +#include "material.inl" + +#endif //!__cplusplus +#endif //!!AI_MATERIAL_H_INC diff --git a/Assimp/include/assimp/material.inl b/Assimp/include/assimp/material.inl new file mode 100644 index 0000000..2c31fd5 --- /dev/null +++ b/Assimp/include/assimp/material.inl @@ -0,0 +1,389 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 material.inl + * @brief Defines the C++ getters for the material system + */ + +#pragma once +#ifndef AI_MATERIAL_INL_INC +#define AI_MATERIAL_INL_INC + +// --------------------------------------------------------------------------- +inline aiPropertyTypeInfo ai_real_to_property_type_info(float) +{ + return aiPTI_Float; +} + +inline aiPropertyTypeInfo ai_real_to_property_type_info(double) +{ + return aiPTI_Double; +} +// --------------------------------------------------------------------------- + +//! @cond never + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::GetTexture( aiTextureType type, + unsigned int index, + C_STRUCT aiString* path, + aiTextureMapping* mapping /*= NULL*/, + unsigned int* uvindex /*= NULL*/, + ai_real* blend /*= NULL*/, + aiTextureOp* op /*= NULL*/, + aiTextureMapMode* mapmode /*= NULL*/) const +{ + return ::aiGetMaterialTexture(this,type,index,path,mapping,uvindex,blend,op,mapmode); +} + +// --------------------------------------------------------------------------- +inline unsigned int aiMaterial::GetTextureCount(aiTextureType type) const +{ + return ::aiGetMaterialTextureCount(this,type); +} + +// --------------------------------------------------------------------------- +template +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx, Type* pOut, + unsigned int* pMax) const +{ + unsigned int iNum = pMax ? *pMax : 1; + + const aiMaterialProperty* prop; + const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx, + (const aiMaterialProperty**)&prop); + if ( AI_SUCCESS == ret ) { + + if (prop->mDataLength < sizeof(Type)*iNum) { + return AI_FAILURE; + } + + if (prop->mType != aiPTI_Buffer) { + return AI_FAILURE; + } + + iNum = std::min((size_t)iNum,prop->mDataLength / sizeof(Type)); + ::memcpy(pOut,prop->mData,iNum * sizeof(Type)); + if (pMax) { + *pMax = iNum; + } + } + return ret; +} + +// --------------------------------------------------------------------------- +template +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,Type& pOut) const +{ + const aiMaterialProperty* prop; + const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx, + (const aiMaterialProperty**)&prop); + if ( AI_SUCCESS == ret ) { + + if (prop->mDataLength < sizeof(Type)) { + return AI_FAILURE; + } + + if (prop->mType != aiPTI_Buffer) { + return AI_FAILURE; + } + + ::memcpy(&pOut,prop->mData,sizeof(Type)); + } + return ret; +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,ai_real* pOut, + unsigned int* pMax) const +{ + return ::aiGetMaterialFloatArray(this,pKey,type,idx,pOut,pMax); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,int* pOut, + unsigned int* pMax) const +{ + return ::aiGetMaterialIntegerArray(this,pKey,type,idx,pOut,pMax); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,ai_real& pOut) const +{ + return aiGetMaterialFloat(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,int& pOut) const +{ + return aiGetMaterialInteger(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiColor4D& pOut) const +{ + return aiGetMaterialColor(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiColor3D& pOut) const +{ + aiColor4D c; + const aiReturn ret = aiGetMaterialColor(this,pKey,type,idx,&c); + pOut = aiColor3D(c.r,c.g,c.b); + return ret; +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiString& pOut) const +{ + return aiGetMaterialString(this,pKey,type,idx,&pOut); +} +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type, + unsigned int idx,aiUVTransform& pOut) const +{ + return aiGetMaterialUVTransform(this,pKey,type,idx,&pOut); +} + + +// --------------------------------------------------------------------------- +template +aiReturn aiMaterial::AddProperty (const TYPE* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(TYPE), + pKey,type,index,aiPTI_Buffer); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const float* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(float), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const double* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(double), + pKey,type,index,aiPTI_Double); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiUVTransform* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiUVTransform), + pKey,type,index,ai_real_to_property_type_info(pInput->mRotation)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiColor4D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor4D), + pKey,type,index,ai_real_to_property_type_info(pInput->a)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiColor3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor3D), + pKey,type,index,ai_real_to_property_type_info(pInput->b)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const aiVector3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiVector3D), + pKey,type,index,ai_real_to_property_type_info(pInput->x)); +} + +// --------------------------------------------------------------------------- +inline aiReturn aiMaterial::AddProperty(const int* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(int), + pKey,type,index,aiPTI_Integer); +} + + +// --------------------------------------------------------------------------- +// The template specializations below are for backwards compatibility. +// The recommended way to add material properties is using the non-template +// overloads. +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const float* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(float), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const double* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(double), + pKey,type,index,aiPTI_Double); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const aiUVTransform* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiUVTransform), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const aiColor4D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor4D), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const aiColor3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiColor3D), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const aiVector3D* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(aiVector3D), + pKey,type,index,aiPTI_Float); +} + +// --------------------------------------------------------------------------- +template<> +inline aiReturn aiMaterial::AddProperty(const int* pInput, + const unsigned int pNumValues, + const char* pKey, + unsigned int type, + unsigned int index) +{ + return AddBinaryProperty((const void*)pInput, + pNumValues * sizeof(int), + pKey,type,index,aiPTI_Integer); +} + +//! @endcond + +#endif //! AI_MATERIAL_INL_INC diff --git a/Assimp/include/assimp/matrix3x3.h b/Assimp/include/assimp/matrix3x3.h new file mode 100644 index 0000000..3cf575e --- /dev/null +++ b/Assimp/include/assimp/matrix3x3.h @@ -0,0 +1,182 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 matrix3x3.h + * @brief Definition of a 3x3 matrix, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_MATRIX3X3_H_INC +#define AI_MATRIX3X3_H_INC + +#include "defs.h" + +#ifdef __cplusplus + +template class aiMatrix4x4t; +template class aiVector2t; + +// --------------------------------------------------------------------------- +/** @brief Represents a row-major 3x3 matrix + * + * There's much confusion about matrix layouts (column vs. row order). + * This is *always* a row-major matrix. Not even with the + * #aiProcess_ConvertToLeftHanded flag, which absolutely does not affect + * matrix order - it just affects the handedness of the coordinate system + * defined thereby. + */ +template +class aiMatrix3x3t +{ +public: + + aiMatrix3x3t () : + a1(static_cast(1.0f)), a2(), a3(), + b1(), b2(static_cast(1.0f)), b3(), + c1(), c2(), c3(static_cast(1.0f)) {} + + aiMatrix3x3t ( TReal _a1, TReal _a2, TReal _a3, + TReal _b1, TReal _b2, TReal _b3, + TReal _c1, TReal _c2, TReal _c3) : + a1(_a1), a2(_a2), a3(_a3), + b1(_b1), b2(_b2), b3(_b3), + c1(_c1), c2(_c2), c3(_c3) + {} + +public: + + // matrix multiplication. + aiMatrix3x3t& operator *= (const aiMatrix3x3t& m); + aiMatrix3x3t operator * (const aiMatrix3x3t& m) const; + + // array access operators + TReal* operator[] (unsigned int p_iIndex); + const TReal* operator[] (unsigned int p_iIndex) const; + + // comparison operators + bool operator== (const aiMatrix4x4t& m) const; + bool operator!= (const aiMatrix4x4t& m) const; + + bool Equal(const aiMatrix4x4t& m, TReal epsilon = 1e-6) const; + + template + operator aiMatrix3x3t () const; + +public: + + // ------------------------------------------------------------------- + /** @brief Construction from a 4x4 matrix. The remaining parts + * of the matrix are ignored. + */ + explicit aiMatrix3x3t( const aiMatrix4x4t& pMatrix); + + // ------------------------------------------------------------------- + /** @brief Transpose the matrix + */ + aiMatrix3x3t& Transpose(); + + // ------------------------------------------------------------------- + /** @brief Invert the matrix. + * If the matrix is not invertible all elements are set to qnan. + * Beware, use (f != f) to check whether a TReal f is qnan. + */ + aiMatrix3x3t& Inverse(); + TReal Determinant() const; + +public: + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around z + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix3x3t& RotationZ(TReal a, aiMatrix3x3t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around + * an arbitrary axis. + * + * @param a Rotation angle, in radians + * @param axis Axis to rotate around + * @param out To be filled + */ + static aiMatrix3x3t& Rotation( TReal a, + const aiVector3t& axis, aiMatrix3x3t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a translation matrix + * @param v Translation vector + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix3x3t& Translation( const aiVector2t& v, aiMatrix3x3t& out); + + // ------------------------------------------------------------------- + /** @brief A function for creating a rotation matrix that rotates a + * vector called "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in column-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ + static aiMatrix3x3t& FromToMatrix(const aiVector3t& from, + const aiVector3t& to, aiMatrix3x3t& out); + +public: + TReal a1, a2, a3; + TReal b1, b2, b3; + TReal c1, c2, c3; +}; + +typedef aiMatrix3x3t aiMatrix3x3; + +#else + +struct aiMatrix3x3 { + ai_real a1, a2, a3; + ai_real b1, b2, b3; + ai_real c1, c2, c3; +}; + +#endif // __cplusplus + +#endif // AI_MATRIX3X3_H_INC diff --git a/Assimp/include/assimp/matrix3x3.inl b/Assimp/include/assimp/matrix3x3.inl new file mode 100644 index 0000000..14f2cd2 --- /dev/null +++ b/Assimp/include/assimp/matrix3x3.inl @@ -0,0 +1,356 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 matrix3x3.inl + * @brief Inline implementation of the 3x3 matrix operators + */ +#pragma once +#ifndef AI_MATRIX3X3_INL_INC +#define AI_MATRIX3X3_INL_INC + +#ifdef __cplusplus +#include "matrix3x3.h" + +#include "matrix4x4.h" +#include +#include +#include + +// ------------------------------------------------------------------------------------------------ +// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. +template +inline aiMatrix3x3t::aiMatrix3x3t( const aiMatrix4x4t& pMatrix) +{ + a1 = pMatrix.a1; a2 = pMatrix.a2; a3 = pMatrix.a3; + b1 = pMatrix.b1; b2 = pMatrix.b2; b3 = pMatrix.b3; + c1 = pMatrix.c1; c2 = pMatrix.c2; c3 = pMatrix.c3; +} + +// ------------------------------------------------------------------------------------------------ +template +inline aiMatrix3x3t& aiMatrix3x3t::operator *= (const aiMatrix3x3t& m) +{ + *this = aiMatrix3x3t(m.a1 * a1 + m.b1 * a2 + m.c1 * a3, + m.a2 * a1 + m.b2 * a2 + m.c2 * a3, + m.a3 * a1 + m.b3 * a2 + m.c3 * a3, + m.a1 * b1 + m.b1 * b2 + m.c1 * b3, + m.a2 * b1 + m.b2 * b2 + m.c2 * b3, + m.a3 * b1 + m.b3 * b2 + m.c3 * b3, + m.a1 * c1 + m.b1 * c2 + m.c1 * c3, + m.a2 * c1 + m.b2 * c2 + m.c2 * c3, + m.a3 * c1 + m.b3 * c2 + m.c3 * c3); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +template +aiMatrix3x3t::operator aiMatrix3x3t () const +{ + return aiMatrix3x3t(static_cast(a1),static_cast(a2),static_cast(a3), + static_cast(b1),static_cast(b2),static_cast(b3), + static_cast(c1),static_cast(c2),static_cast(c3)); +} + +// ------------------------------------------------------------------------------------------------ +template +inline aiMatrix3x3t aiMatrix3x3t::operator* (const aiMatrix3x3t& m) const +{ + aiMatrix3x3t temp( *this); + temp *= m; + return temp; +} + +// ------------------------------------------------------------------------------------------------ +template +inline TReal* aiMatrix3x3t::operator[] (unsigned int p_iIndex) { + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + default: + break; + } + return &a1; +} + +// ------------------------------------------------------------------------------------------------ +template +inline const TReal* aiMatrix3x3t::operator[] (unsigned int p_iIndex) const { + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + default: + break; + } + return &a1; +} + +// ------------------------------------------------------------------------------------------------ +template +inline bool aiMatrix3x3t::operator== (const aiMatrix4x4t& m) const +{ + return a1 == m.a1 && a2 == m.a2 && a3 == m.a3 && + b1 == m.b1 && b2 == m.b2 && b3 == m.b3 && + c1 == m.c1 && c2 == m.c2 && c3 == m.c3; +} + +// ------------------------------------------------------------------------------------------------ +template +inline bool aiMatrix3x3t::operator!= (const aiMatrix4x4t& m) const +{ + return !(*this == m); +} + +// --------------------------------------------------------------------------- +template +inline bool aiMatrix3x3t::Equal(const aiMatrix4x4t& m, TReal epsilon) const { + return + std::abs(a1 - m.a1) <= epsilon && + std::abs(a2 - m.a2) <= epsilon && + std::abs(a3 - m.a3) <= epsilon && + std::abs(b1 - m.b1) <= epsilon && + std::abs(b2 - m.b2) <= epsilon && + std::abs(b3 - m.b3) <= epsilon && + std::abs(c1 - m.c1) <= epsilon && + std::abs(c2 - m.c2) <= epsilon && + std::abs(c3 - m.c3) <= epsilon; +} + +// ------------------------------------------------------------------------------------------------ +template +inline aiMatrix3x3t& aiMatrix3x3t::Transpose() +{ + // (TReal&) don't remove, GCC complains cause of packed fields + std::swap( (TReal&)a2, (TReal&)b1); + std::swap( (TReal&)a3, (TReal&)c1); + std::swap( (TReal&)b3, (TReal&)c2); + return *this; +} + +// ---------------------------------------------------------------------------------------- +template +inline TReal aiMatrix3x3t::Determinant() const +{ + return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 + a3*b1*c2 - a3*b2*c1; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix3x3t& aiMatrix3x3t::Inverse() +{ + // Compute the reciprocal determinant + TReal det = Determinant(); + if(det == static_cast(0.0)) + { + // Matrix not invertible. Setting all elements to nan is not really + // correct in a mathematical sense; but at least qnans are easy to + // spot. XXX we might throw an exception instead, which would + // be even much better to spot :/. + const TReal nan = std::numeric_limits::quiet_NaN(); + *this = aiMatrix3x3t( nan,nan,nan,nan,nan,nan,nan,nan,nan); + + return *this; + } + + TReal invdet = static_cast(1.0) / det; + + aiMatrix3x3t res; + res.a1 = invdet * (b2 * c3 - b3 * c2); + res.a2 = -invdet * (a2 * c3 - a3 * c2); + res.a3 = invdet * (a2 * b3 - a3 * b2); + res.b1 = -invdet * (b1 * c3 - b3 * c1); + res.b2 = invdet * (a1 * c3 - a3 * c1); + res.b3 = -invdet * (a1 * b3 - a3 * b1); + res.c1 = invdet * (b1 * c2 - b2 * c1); + res.c2 = -invdet * (a1 * c2 - a2 * c1); + res.c3 = invdet * (a1 * b2 - a2 * b1); + *this = res; + + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +inline aiMatrix3x3t& aiMatrix3x3t::RotationZ(TReal a, aiMatrix3x3t& out) +{ + out.a1 = out.b2 = std::cos(a); + out.b1 = std::sin(a); + out.a2 = - out.b1; + + out.a3 = out.b3 = out.c1 = out.c2 = 0.f; + out.c3 = 1.f; + + return out; +} + +// ------------------------------------------------------------------------------------------------ +// Returns a rotation matrix for a rotation around an arbitrary axis. +template +inline aiMatrix3x3t& aiMatrix3x3t::Rotation( TReal a, const aiVector3t& axis, aiMatrix3x3t& out) +{ + TReal c = std::cos( a), s = std::sin( a), t = 1 - c; + TReal x = axis.x, y = axis.y, z = axis.z; + + // Many thanks to MathWorld and Wikipedia + out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y; + out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x; + out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c; + + return out; +} + +// ------------------------------------------------------------------------------------------------ +template +inline aiMatrix3x3t& aiMatrix3x3t::Translation( const aiVector2t& v, aiMatrix3x3t& out) +{ + out = aiMatrix3x3t(); + out.a3 = v.x; + out.b3 = v.y; + return out; +} + +// ---------------------------------------------------------------------------------------- +/** A function for creating a rotation matrix that rotates a vector called + * "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in colum-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix3x3t& aiMatrix3x3t::FromToMatrix(const aiVector3t& from, + const aiVector3t& to, aiMatrix3x3t& mtx) +{ + const TReal e = from * to; + const TReal f = (e < 0)? -e:e; + + if (f > static_cast(1.0) - static_cast(0.00001)) /* "from" and "to"-vector almost parallel */ + { + aiVector3D u,v; /* temporary storage vectors */ + aiVector3D x; /* vector most nearly orthogonal to "from" */ + + x.x = (from.x > 0.0)? from.x : -from.x; + x.y = (from.y > 0.0)? from.y : -from.y; + x.z = (from.z > 0.0)? from.z : -from.z; + + if (x.x < x.y) + { + if (x.x < x.z) + { + x.x = static_cast(1.0); + x.y = x.z = static_cast(0.0); + } + else + { + x.z = static_cast(1.0); + x.x = x.y = static_cast(0.0); + } + } + else + { + if (x.y < x.z) + { + x.y = static_cast(1.0); + x.x = x.z = static_cast(0.0); + } + else + { + x.z = static_cast(1.0); + x.x = x.y = static_cast(0.0); + } + } + + u.x = x.x - from.x; u.y = x.y - from.y; u.z = x.z - from.z; + v.x = x.x - to.x; v.y = x.y - to.y; v.z = x.z - to.z; + + const TReal c1 = static_cast(2.0) / (u * u); + const TReal c2 = static_cast(2.0) / (v * v); + const TReal c3 = c1 * c2 * (u * v); + + for (unsigned int i = 0; i < 3; i++) + { + for (unsigned int j = 0; j < 3; j++) + { + mtx[i][j] = - c1 * u[i] * u[j] - c2 * v[i] * v[j] + + c3 * v[i] * u[j]; + } + mtx[i][i] += static_cast(1.0); + } + } + else /* the most common case, unless "from"="to", or "from"=-"to" */ + { + const aiVector3D v = from ^ to; + /* ... use this hand optimized version (9 mults less) */ + const TReal h = static_cast(1.0)/(static_cast(1.0) + e); /* optimization by Gottfried Chen */ + const TReal hvx = h * v.x; + const TReal hvz = h * v.z; + const TReal hvxy = hvx * v.y; + const TReal hvxz = hvx * v.z; + const TReal hvyz = hvz * v.y; + mtx[0][0] = e + hvx * v.x; + mtx[0][1] = hvxy - v.z; + mtx[0][2] = hvxz + v.y; + + mtx[1][0] = hvxy + v.z; + mtx[1][1] = e + h * v.y * v.y; + mtx[1][2] = hvyz - v.x; + + mtx[2][0] = hvxz - v.y; + mtx[2][1] = hvyz + v.x; + mtx[2][2] = e + hvz * v.z; + } + return mtx; +} + + +#endif // __cplusplus +#endif // AI_MATRIX3X3_INL_INC diff --git a/Assimp/include/assimp/matrix4x4.h b/Assimp/include/assimp/matrix4x4.h new file mode 100644 index 0000000..4311fa1 --- /dev/null +++ b/Assimp/include/assimp/matrix4x4.h @@ -0,0 +1,279 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 matrix4x4.h + * @brief 4x4 matrix structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_MATRIX4X4_H_INC +#define AI_MATRIX4X4_H_INC + +#include "vector3.h" +#include "defs.h" + +#ifdef __cplusplus + +template class aiMatrix3x3t; +template class aiQuaterniont; + +// --------------------------------------------------------------------------- +/** @brief Represents a row-major 4x4 matrix, use this for homogeneous + * coordinates. + * + * There's much confusion about matrix layouts (column vs. row order). + * This is *always* a row-major matrix. Not even with the + * #aiProcess_ConvertToLeftHanded flag, which absolutely does not affect + * matrix order - it just affects the handedness of the coordinate system + * defined thereby. + */ +template +class aiMatrix4x4t +{ +public: + + /** set to identity */ + aiMatrix4x4t (); + + /** construction from single values */ + aiMatrix4x4t ( TReal _a1, TReal _a2, TReal _a3, TReal _a4, + TReal _b1, TReal _b2, TReal _b3, TReal _b4, + TReal _c1, TReal _c2, TReal _c3, TReal _c4, + TReal _d1, TReal _d2, TReal _d3, TReal _d4); + + + /** construction from 3x3 matrix, remaining elements are set to identity */ + explicit aiMatrix4x4t( const aiMatrix3x3t& m); + + /** construction from position, rotation and scaling components + * @param scaling The scaling for the x,y,z axes + * @param rotation The rotation as a hamilton quaternion + * @param position The position for the x,y,z axes + */ + aiMatrix4x4t(const aiVector3t& scaling, const aiQuaterniont& rotation, + const aiVector3t& position); + +public: + + // array access operators + /** @fn TReal* operator[] (unsigned int p_iIndex) + * @param [in] p_iIndex - index of the row. + * @return pointer to pointed row. + */ + TReal* operator[] (unsigned int p_iIndex); + + /** @fn const TReal* operator[] (unsigned int p_iIndex) const + * @overload TReal* operator[] (unsigned int p_iIndex) + */ + const TReal* operator[] (unsigned int p_iIndex) const; + + // comparison operators + bool operator== (const aiMatrix4x4t& m) const; + bool operator!= (const aiMatrix4x4t& m) const; + + bool Equal(const aiMatrix4x4t& m, TReal epsilon = 1e-6) const; + + // matrix multiplication. + aiMatrix4x4t& operator *= (const aiMatrix4x4t& m); + aiMatrix4x4t operator * (const aiMatrix4x4t& m) const; + aiMatrix4x4t operator * (const TReal& aFloat) const; + aiMatrix4x4t operator + (const aiMatrix4x4t& aMatrix) const; + + template + operator aiMatrix4x4t () const; + +public: + + // ------------------------------------------------------------------- + /** @brief Transpose the matrix */ + aiMatrix4x4t& Transpose(); + + // ------------------------------------------------------------------- + /** @brief Invert the matrix. + * If the matrix is not invertible all elements are set to qnan. + * Beware, use (f != f) to check whether a TReal f is qnan. + */ + aiMatrix4x4t& Inverse(); + TReal Determinant() const; + + + // ------------------------------------------------------------------- + /** @brief Returns true of the matrix is the identity matrix. + * The check is performed against a not so small epsilon. + */ + inline bool IsIdentity() const; + + // ------------------------------------------------------------------- + /** @brief Decompose a trafo matrix into its original components + * @param scaling Receives the output scaling for the x,y,z axes + * @param rotation Receives the output rotation as a hamilton + * quaternion + * @param position Receives the output position for the x,y,z axes + */ + void Decompose (aiVector3t& scaling, aiQuaterniont& rotation, + aiVector3t& position) const; + + // ------------------------------------------------------------------- + /** @fn void Decompose(aiVector3t& pScaling, aiVector3t& pRotation, aiVector3t& pPosition) const + * @brief Decompose a trafo matrix into its original components. + * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat + * @param [out] pScaling - Receives the output scaling for the x,y,z axes. + * @param [out] pRotation - Receives the output rotation as a Euler angles. + * @param [out] pPosition - Receives the output position for the x,y,z axes. + */ + void Decompose(aiVector3t& pScaling, aiVector3t& pRotation, aiVector3t& pPosition) const; + + // ------------------------------------------------------------------- + /** @fn void Decompose(aiVector3t& pScaling, aiVector3t& pRotationAxis, TReal& pRotationAngle, aiVector3t& pPosition) const + * @brief Decompose a trafo matrix into its original components + * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat + * @param [out] pScaling - Receives the output scaling for the x,y,z axes. + * @param [out] pRotationAxis - Receives the output rotation axis. + * @param [out] pRotationAngle - Receives the output rotation angle for @ref pRotationAxis. + * @param [out] pPosition - Receives the output position for the x,y,z axes. + */ + void Decompose(aiVector3t& pScaling, aiVector3t& pRotationAxis, TReal& pRotationAngle, aiVector3t& pPosition) const; + + // ------------------------------------------------------------------- + /** @brief Decompose a trafo matrix with no scaling into its + * original components + * @param rotation Receives the output rotation as a hamilton + * quaternion + * @param position Receives the output position for the x,y,z axes + */ + void DecomposeNoScaling (aiQuaterniont& rotation, + aiVector3t& position) const; + + + // ------------------------------------------------------------------- + /** @brief Creates a trafo matrix from a set of euler angles + * @param x Rotation angle for the x-axis, in radians + * @param y Rotation angle for the y-axis, in radians + * @param z Rotation angle for the z-axis, in radians + */ + aiMatrix4x4t& FromEulerAnglesXYZ(TReal x, TReal y, TReal z); + aiMatrix4x4t& FromEulerAnglesXYZ(const aiVector3t& blubb); + +public: + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around the x axis + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& RotationX(TReal a, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around the y axis + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& RotationY(TReal a, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a rotation matrix for a rotation around the z axis + * @param a Rotation angle, in radians + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& RotationZ(TReal a, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** Returns a rotation matrix for a rotation around an arbitrary axis. + * @param a Rotation angle, in radians + * @param axis Rotation axis, should be a normalized vector. + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& Rotation(TReal a, const aiVector3t& axis, + aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a translation matrix + * @param v Translation vector + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& Translation( const aiVector3t& v, + aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief Returns a scaling matrix + * @param v Scaling vector + * @param out Receives the output matrix + * @return Reference to the output matrix + */ + static aiMatrix4x4t& Scaling( const aiVector3t& v, aiMatrix4x4t& out); + + // ------------------------------------------------------------------- + /** @brief A function for creating a rotation matrix that rotates a + * vector called "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in column-major form + * Authors: Tomas Mueller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ + static aiMatrix4x4t& FromToMatrix(const aiVector3t& from, + const aiVector3t& to, aiMatrix4x4t& out); + +public: + TReal a1, a2, a3, a4; + TReal b1, b2, b3, b4; + TReal c1, c2, c3, c4; + TReal d1, d2, d3, d4; +}; + +typedef aiMatrix4x4t aiMatrix4x4; + +#else + +struct aiMatrix4x4 { + ai_real a1, a2, a3, a4; + ai_real b1, b2, b3, b4; + ai_real c1, c2, c3, c4; + ai_real d1, d2, d3, d4; +}; + + +#endif // __cplusplus + +#endif // AI_MATRIX4X4_H_INC diff --git a/Assimp/include/assimp/matrix4x4.inl b/Assimp/include/assimp/matrix4x4.inl new file mode 100644 index 0000000..b15d50a --- /dev/null +++ b/Assimp/include/assimp/matrix4x4.inl @@ -0,0 +1,681 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 matrix4x4.inl + * @brief Inline implementation of the 4x4 matrix operators + */ +#pragma once +#ifndef AI_MATRIX4X4_INL_INC +#define AI_MATRIX4X4_INL_INC + +#ifdef __cplusplus + +#include "matrix4x4.h" +#include "matrix3x3.h" +#include "quaternion.h" + +#include +#include +#include + +// ---------------------------------------------------------------------------------------- +template +aiMatrix4x4t ::aiMatrix4x4t () : + a1(1.0f), a2(), a3(), a4(), + b1(), b2(1.0f), b3(), b4(), + c1(), c2(), c3(1.0f), c4(), + d1(), d2(), d3(), d4(1.0f) +{ + +} + +// ---------------------------------------------------------------------------------------- +template +aiMatrix4x4t ::aiMatrix4x4t (TReal _a1, TReal _a2, TReal _a3, TReal _a4, + TReal _b1, TReal _b2, TReal _b3, TReal _b4, + TReal _c1, TReal _c2, TReal _c3, TReal _c4, + TReal _d1, TReal _d2, TReal _d3, TReal _d4) : + a1(_a1), a2(_a2), a3(_a3), a4(_a4), + b1(_b1), b2(_b2), b3(_b3), b4(_b4), + c1(_c1), c2(_c2), c3(_c3), c4(_c4), + d1(_d1), d2(_d2), d3(_d3), d4(_d4) +{ + +} + +// ------------------------------------------------------------------------------------------------ +template +template +aiMatrix4x4t::operator aiMatrix4x4t () const +{ + return aiMatrix4x4t(static_cast(a1),static_cast(a2),static_cast(a3),static_cast(a4), + static_cast(b1),static_cast(b2),static_cast(b3),static_cast(b4), + static_cast(c1),static_cast(c2),static_cast(c3),static_cast(c4), + static_cast(d1),static_cast(d2),static_cast(d3),static_cast(d4)); +} + + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t::aiMatrix4x4t (const aiMatrix3x3t& m) +{ + a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = static_cast(0.0); + b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = static_cast(0.0); + c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = static_cast(0.0); + d1 = static_cast(0.0); d2 = static_cast(0.0); d3 = static_cast(0.0); d4 = static_cast(1.0); +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t::aiMatrix4x4t (const aiVector3t& scaling, const aiQuaterniont& rotation, const aiVector3t& position) +{ + // build a 3x3 rotation matrix + aiMatrix3x3t m = rotation.GetMatrix(); + + a1 = m.a1 * scaling.x; + a2 = m.a2 * scaling.x; + a3 = m.a3 * scaling.x; + a4 = position.x; + + b1 = m.b1 * scaling.y; + b2 = m.b2 * scaling.y; + b3 = m.b3 * scaling.y; + b4 = position.y; + + c1 = m.c1 * scaling.z; + c2 = m.c2 * scaling.z; + c3 = m.c3 * scaling.z; + c4= position.z; + + d1 = static_cast(0.0); + d2 = static_cast(0.0); + d3 = static_cast(0.0); + d4 = static_cast(1.0); +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::operator *= (const aiMatrix4x4t& m) +{ + *this = aiMatrix4x4t( + m.a1 * a1 + m.b1 * a2 + m.c1 * a3 + m.d1 * a4, + m.a2 * a1 + m.b2 * a2 + m.c2 * a3 + m.d2 * a4, + m.a3 * a1 + m.b3 * a2 + m.c3 * a3 + m.d3 * a4, + m.a4 * a1 + m.b4 * a2 + m.c4 * a3 + m.d4 * a4, + m.a1 * b1 + m.b1 * b2 + m.c1 * b3 + m.d1 * b4, + m.a2 * b1 + m.b2 * b2 + m.c2 * b3 + m.d2 * b4, + m.a3 * b1 + m.b3 * b2 + m.c3 * b3 + m.d3 * b4, + m.a4 * b1 + m.b4 * b2 + m.c4 * b3 + m.d4 * b4, + m.a1 * c1 + m.b1 * c2 + m.c1 * c3 + m.d1 * c4, + m.a2 * c1 + m.b2 * c2 + m.c2 * c3 + m.d2 * c4, + m.a3 * c1 + m.b3 * c2 + m.c3 * c3 + m.d3 * c4, + m.a4 * c1 + m.b4 * c2 + m.c4 * c3 + m.d4 * c4, + m.a1 * d1 + m.b1 * d2 + m.c1 * d3 + m.d1 * d4, + m.a2 * d1 + m.b2 * d2 + m.c2 * d3 + m.d2 * d4, + m.a3 * d1 + m.b3 * d2 + m.c3 * d3 + m.d3 * d4, + m.a4 * d1 + m.b4 * d2 + m.c4 * d3 + m.d4 * d4); + return *this; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t aiMatrix4x4t::operator* (const TReal& aFloat) const +{ + aiMatrix4x4t temp( + a1 * aFloat, + a2 * aFloat, + a3 * aFloat, + a4 * aFloat, + b1 * aFloat, + b2 * aFloat, + b3 * aFloat, + b4 * aFloat, + c1 * aFloat, + c2 * aFloat, + c3 * aFloat, + c4 * aFloat, + d1 * aFloat, + d2 * aFloat, + d3 * aFloat, + d4 * aFloat); + return temp; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t aiMatrix4x4t::operator+ (const aiMatrix4x4t& m) const +{ + aiMatrix4x4t temp( + m.a1 + a1, + m.a2 + a2, + m.a3 + a3, + m.a4 + a4, + m.b1 + b1, + m.b2 + b2, + m.b3 + b3, + m.b4 + b4, + m.c1 + c1, + m.c2 + c2, + m.c3 + c3, + m.c4 + c4, + m.d1 + d1, + m.d2 + d2, + m.d3 + d3, + m.d4 + d4); + return temp; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t aiMatrix4x4t::operator* (const aiMatrix4x4t& m) const +{ + aiMatrix4x4t temp( *this); + temp *= m; + return temp; +} + + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::Transpose() +{ + // (TReal&) don't remove, GCC complains cause of packed fields + std::swap( (TReal&)b1, (TReal&)a2); + std::swap( (TReal&)c1, (TReal&)a3); + std::swap( (TReal&)c2, (TReal&)b3); + std::swap( (TReal&)d1, (TReal&)a4); + std::swap( (TReal&)d2, (TReal&)b4); + std::swap( (TReal&)d3, (TReal&)c4); + return *this; +} + + +// ---------------------------------------------------------------------------------------- +template +inline TReal aiMatrix4x4t::Determinant() const +{ + return a1*b2*c3*d4 - a1*b2*c4*d3 + a1*b3*c4*d2 - a1*b3*c2*d4 + + a1*b4*c2*d3 - a1*b4*c3*d2 - a2*b3*c4*d1 + a2*b3*c1*d4 + - a2*b4*c1*d3 + a2*b4*c3*d1 - a2*b1*c3*d4 + a2*b1*c4*d3 + + a3*b4*c1*d2 - a3*b4*c2*d1 + a3*b1*c2*d4 - a3*b1*c4*d2 + + a3*b2*c4*d1 - a3*b2*c1*d4 - a4*b1*c2*d3 + a4*b1*c3*d2 + - a4*b2*c3*d1 + a4*b2*c1*d3 - a4*b3*c1*d2 + a4*b3*c2*d1; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::Inverse() +{ + // Compute the reciprocal determinant + const TReal det = Determinant(); + if(det == static_cast(0.0)) + { + // Matrix not invertible. Setting all elements to nan is not really + // correct in a mathematical sense but it is easy to debug for the + // programmer. + const TReal nan = std::numeric_limits::quiet_NaN(); + *this = aiMatrix4x4t( + nan,nan,nan,nan, + nan,nan,nan,nan, + nan,nan,nan,nan, + nan,nan,nan,nan); + + return *this; + } + + const TReal invdet = static_cast(1.0) / det; + + aiMatrix4x4t res; + res.a1 = invdet * (b2 * (c3 * d4 - c4 * d3) + b3 * (c4 * d2 - c2 * d4) + b4 * (c2 * d3 - c3 * d2)); + res.a2 = -invdet * (a2 * (c3 * d4 - c4 * d3) + a3 * (c4 * d2 - c2 * d4) + a4 * (c2 * d3 - c3 * d2)); + res.a3 = invdet * (a2 * (b3 * d4 - b4 * d3) + a3 * (b4 * d2 - b2 * d4) + a4 * (b2 * d3 - b3 * d2)); + res.a4 = -invdet * (a2 * (b3 * c4 - b4 * c3) + a3 * (b4 * c2 - b2 * c4) + a4 * (b2 * c3 - b3 * c2)); + res.b1 = -invdet * (b1 * (c3 * d4 - c4 * d3) + b3 * (c4 * d1 - c1 * d4) + b4 * (c1 * d3 - c3 * d1)); + res.b2 = invdet * (a1 * (c3 * d4 - c4 * d3) + a3 * (c4 * d1 - c1 * d4) + a4 * (c1 * d3 - c3 * d1)); + res.b3 = -invdet * (a1 * (b3 * d4 - b4 * d3) + a3 * (b4 * d1 - b1 * d4) + a4 * (b1 * d3 - b3 * d1)); + res.b4 = invdet * (a1 * (b3 * c4 - b4 * c3) + a3 * (b4 * c1 - b1 * c4) + a4 * (b1 * c3 - b3 * c1)); + res.c1 = invdet * (b1 * (c2 * d4 - c4 * d2) + b2 * (c4 * d1 - c1 * d4) + b4 * (c1 * d2 - c2 * d1)); + res.c2 = -invdet * (a1 * (c2 * d4 - c4 * d2) + a2 * (c4 * d1 - c1 * d4) + a4 * (c1 * d2 - c2 * d1)); + res.c3 = invdet * (a1 * (b2 * d4 - b4 * d2) + a2 * (b4 * d1 - b1 * d4) + a4 * (b1 * d2 - b2 * d1)); + res.c4 = -invdet * (a1 * (b2 * c4 - b4 * c2) + a2 * (b4 * c1 - b1 * c4) + a4 * (b1 * c2 - b2 * c1)); + res.d1 = -invdet * (b1 * (c2 * d3 - c3 * d2) + b2 * (c3 * d1 - c1 * d3) + b3 * (c1 * d2 - c2 * d1)); + res.d2 = invdet * (a1 * (c2 * d3 - c3 * d2) + a2 * (c3 * d1 - c1 * d3) + a3 * (c1 * d2 - c2 * d1)); + res.d3 = -invdet * (a1 * (b2 * d3 - b3 * d2) + a2 * (b3 * d1 - b1 * d3) + a3 * (b1 * d2 - b2 * d1)); + res.d4 = invdet * (a1 * (b2 * c3 - b3 * c2) + a2 * (b3 * c1 - b1 * c3) + a3 * (b1 * c2 - b2 * c1)); + *this = res; + + return *this; +} + +// ---------------------------------------------------------------------------------------- +template +inline TReal* aiMatrix4x4t::operator[](unsigned int p_iIndex) { + if (p_iIndex > 3) { + return NULL; + } + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + case 3: + return &d1; + default: + break; + } + return &a1; +} + +// ---------------------------------------------------------------------------------------- +template +inline const TReal* aiMatrix4x4t::operator[](unsigned int p_iIndex) const { + if (p_iIndex > 3) { + return NULL; + } + + switch ( p_iIndex ) { + case 0: + return &a1; + case 1: + return &b1; + case 2: + return &c1; + case 3: + return &d1; + default: + break; + } + return &a1; +} + +// ---------------------------------------------------------------------------------------- +template +inline bool aiMatrix4x4t::operator== (const aiMatrix4x4t& m) const +{ + return (a1 == m.a1 && a2 == m.a2 && a3 == m.a3 && a4 == m.a4 && + b1 == m.b1 && b2 == m.b2 && b3 == m.b3 && b4 == m.b4 && + c1 == m.c1 && c2 == m.c2 && c3 == m.c3 && c4 == m.c4 && + d1 == m.d1 && d2 == m.d2 && d3 == m.d3 && d4 == m.d4); +} + +// ---------------------------------------------------------------------------------------- +template +inline bool aiMatrix4x4t::operator!= (const aiMatrix4x4t& m) const +{ + return !(*this == m); +} + +// --------------------------------------------------------------------------- +template +inline bool aiMatrix4x4t::Equal(const aiMatrix4x4t& m, TReal epsilon) const { + return + std::abs(a1 - m.a1) <= epsilon && + std::abs(a2 - m.a2) <= epsilon && + std::abs(a3 - m.a3) <= epsilon && + std::abs(a4 - m.a4) <= epsilon && + std::abs(b1 - m.b1) <= epsilon && + std::abs(b2 - m.b2) <= epsilon && + std::abs(b3 - m.b3) <= epsilon && + std::abs(b4 - m.b4) <= epsilon && + std::abs(c1 - m.c1) <= epsilon && + std::abs(c2 - m.c2) <= epsilon && + std::abs(c3 - m.c3) <= epsilon && + std::abs(c4 - m.c4) <= epsilon && + std::abs(d1 - m.d1) <= epsilon && + std::abs(d2 - m.d2) <= epsilon && + std::abs(d3 - m.d3) <= epsilon && + std::abs(d4 - m.d4) <= epsilon; +} + +// ---------------------------------------------------------------------------------------- + +#define ASSIMP_MATRIX4_4_DECOMPOSE_PART \ + const aiMatrix4x4t& _this = *this;/* Create alias for conveniance. */ \ + \ + /* extract translation */ \ + pPosition.x = _this[0][3]; \ + pPosition.y = _this[1][3]; \ + pPosition.z = _this[2][3]; \ + \ + /* extract the columns of the matrix. */ \ + aiVector3t vCols[3] = { \ + aiVector3t(_this[0][0],_this[1][0],_this[2][0]), \ + aiVector3t(_this[0][1],_this[1][1],_this[2][1]), \ + aiVector3t(_this[0][2],_this[1][2],_this[2][2]) \ + }; \ + \ + /* extract the scaling factors */ \ + pScaling.x = vCols[0].Length(); \ + pScaling.y = vCols[1].Length(); \ + pScaling.z = vCols[2].Length(); \ + \ + /* and the sign of the scaling */ \ + if (Determinant() < 0) pScaling = -pScaling; \ + \ + /* and remove all scaling from the matrix */ \ + if(pScaling.x) vCols[0] /= pScaling.x; \ + if(pScaling.y) vCols[1] /= pScaling.y; \ + if(pScaling.z) vCols[2] /= pScaling.z; \ + \ + do {} while(false) + + + + +template +inline void aiMatrix4x4t::Decompose (aiVector3t& pScaling, aiQuaterniont& pRotation, + aiVector3t& pPosition) const +{ + ASSIMP_MATRIX4_4_DECOMPOSE_PART; + + // build a 3x3 rotation matrix + aiMatrix3x3t m(vCols[0].x,vCols[1].x,vCols[2].x, + vCols[0].y,vCols[1].y,vCols[2].y, + vCols[0].z,vCols[1].z,vCols[2].z); + + // and generate the rotation quaternion from it + pRotation = aiQuaterniont(m); +} + +template +inline void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3t& pRotation, aiVector3t& pPosition) const +{ + ASSIMP_MATRIX4_4_DECOMPOSE_PART; + + /* + | CE -CF D 0 | + M = | BDE+AF -BDF+AE -BC 0 | + | -ADE+BF -ADF+BE AC 0 | + | 0 0 0 1 | + + A = cos(angle_x), B = sin(angle_x); + C = cos(angle_y), D = sin(angle_y); + E = cos(angle_z), F = sin(angle_z); + */ + + // Use a small epsilon to solve floating-point inaccuracies + const TReal epsilon = 10e-3f; + + pRotation.y = std::asin(vCols[2].x);// D. Angle around oY. + + TReal C = std::cos(pRotation.y); + + if(std::fabs(C) > epsilon) + { + // Finding angle around oX. + TReal tan_x = vCols[2].z / C;// A + TReal tan_y = -vCols[2].y / C;// B + + pRotation.x = std::atan2(tan_y, tan_x); + // Finding angle around oZ. + tan_x = vCols[0].x / C;// E + tan_y = -vCols[1].x / C;// F + pRotation.z = std::atan2(tan_y, tan_x); + } + else + {// oY is fixed. + pRotation.x = 0;// Set angle around oX to 0. => A == 1, B == 0, C == 0, D == 1. + + // And finding angle around oZ. + TReal tan_x = vCols[1].y;// -BDF+AE => E + TReal tan_y = vCols[0].y;// BDE+AF => F + + pRotation.z = std::atan2(tan_y, tan_x); + } +} + +#undef ASSIMP_MATRIX4_4_DECOMPOSE_PART + +template +inline void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3t& pRotationAxis, TReal& pRotationAngle, + aiVector3t& pPosition) const +{ +aiQuaterniont pRotation; + + Decompose(pScaling, pRotation, pPosition); + pRotation.Normalize(); + + TReal angle_cos = pRotation.w; + TReal angle_sin = std::sqrt(1.0f - angle_cos * angle_cos); + + pRotationAngle = std::acos(angle_cos) * 2; + + // Use a small epsilon to solve floating-point inaccuracies + const TReal epsilon = 10e-3f; + + if(std::fabs(angle_sin) < epsilon) angle_sin = 1; + + pRotationAxis.x = pRotation.x / angle_sin; + pRotationAxis.y = pRotation.y / angle_sin; + pRotationAxis.z = pRotation.z / angle_sin; +} + +// ---------------------------------------------------------------------------------------- +template +inline void aiMatrix4x4t::DecomposeNoScaling (aiQuaterniont& rotation, + aiVector3t& position) const +{ + const aiMatrix4x4t& _this = *this; + + // extract translation + position.x = _this[0][3]; + position.y = _this[1][3]; + position.z = _this[2][3]; + + // extract rotation + rotation = aiQuaterniont((aiMatrix3x3t)_this); +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::FromEulerAnglesXYZ(const aiVector3t& blubb) +{ + return FromEulerAnglesXYZ(blubb.x,blubb.y,blubb.z); +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::FromEulerAnglesXYZ(TReal x, TReal y, TReal z) +{ + aiMatrix4x4t& _this = *this; + + TReal cr = std::cos( x ); + TReal sr = std::sin( x ); + TReal cp = std::cos( y ); + TReal sp = std::sin( y ); + TReal cy = std::cos( z ); + TReal sy = std::sin( z ); + + _this.a1 = cp*cy ; + _this.a2 = cp*sy; + _this.a3 = -sp ; + + TReal srsp = sr*sp; + TReal crsp = cr*sp; + + _this.b1 = srsp*cy-cr*sy ; + _this.b2 = srsp*sy+cr*cy ; + _this.b3 = sr*cp ; + + _this.c1 = crsp*cy+sr*sy ; + _this.c2 = crsp*sy-sr*cy ; + _this.c3 = cr*cp ; + + return *this; +} + +// ---------------------------------------------------------------------------------------- +template +inline bool aiMatrix4x4t::IsIdentity() const +{ + // Use a small epsilon to solve floating-point inaccuracies + const static TReal epsilon = 10e-3f; + + return (a2 <= epsilon && a2 >= -epsilon && + a3 <= epsilon && a3 >= -epsilon && + a4 <= epsilon && a4 >= -epsilon && + b1 <= epsilon && b1 >= -epsilon && + b3 <= epsilon && b3 >= -epsilon && + b4 <= epsilon && b4 >= -epsilon && + c1 <= epsilon && c1 >= -epsilon && + c2 <= epsilon && c2 >= -epsilon && + c4 <= epsilon && c4 >= -epsilon && + d1 <= epsilon && d1 >= -epsilon && + d2 <= epsilon && d2 >= -epsilon && + d3 <= epsilon && d3 >= -epsilon && + a1 <= 1.f+epsilon && a1 >= 1.f-epsilon && + b2 <= 1.f+epsilon && b2 >= 1.f-epsilon && + c3 <= 1.f+epsilon && c3 >= 1.f-epsilon && + d4 <= 1.f+epsilon && d4 >= 1.f-epsilon); +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::RotationX(TReal a, aiMatrix4x4t& out) +{ + /* + | 1 0 0 0 | + M = | 0 cos(A) -sin(A) 0 | + | 0 sin(A) cos(A) 0 | + | 0 0 0 1 | */ + out = aiMatrix4x4t(); + out.b2 = out.c3 = std::cos(a); + out.b3 = -(out.c2 = std::sin(a)); + return out; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::RotationY(TReal a, aiMatrix4x4t& out) +{ + /* + | cos(A) 0 sin(A) 0 | + M = | 0 1 0 0 | + | -sin(A) 0 cos(A) 0 | + | 0 0 0 1 | + */ + out = aiMatrix4x4t(); + out.a1 = out.c3 = std::cos(a); + out.c1 = -(out.a3 = std::sin(a)); + return out; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::RotationZ(TReal a, aiMatrix4x4t& out) +{ + /* + | cos(A) -sin(A) 0 0 | + M = | sin(A) cos(A) 0 0 | + | 0 0 1 0 | + | 0 0 0 1 | */ + out = aiMatrix4x4t(); + out.a1 = out.b2 = std::cos(a); + out.a2 = -(out.b1 = std::sin(a)); + return out; +} + +// ---------------------------------------------------------------------------------------- +// Returns a rotation matrix for a rotation around an arbitrary axis. +template +inline aiMatrix4x4t& aiMatrix4x4t::Rotation( TReal a, const aiVector3t& axis, aiMatrix4x4t& out) +{ + TReal c = std::cos( a), s = std::sin( a), t = 1 - c; + TReal x = axis.x, y = axis.y, z = axis.z; + + // Many thanks to MathWorld and Wikipedia + out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y; + out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x; + out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c; + out.a4 = out.b4 = out.c4 = static_cast(0.0); + out.d1 = out.d2 = out.d3 = static_cast(0.0); + out.d4 = static_cast(1.0); + + return out; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::Translation( const aiVector3t& v, aiMatrix4x4t& out) +{ + out = aiMatrix4x4t(); + out.a4 = v.x; + out.b4 = v.y; + out.c4 = v.z; + return out; +} + +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::Scaling( const aiVector3t& v, aiMatrix4x4t& out) +{ + out = aiMatrix4x4t(); + out.a1 = v.x; + out.b2 = v.y; + out.c3 = v.z; + return out; +} + +// ---------------------------------------------------------------------------------------- +/** A function for creating a rotation matrix that rotates a vector called + * "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in colum-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ +// ---------------------------------------------------------------------------------------- +template +inline aiMatrix4x4t& aiMatrix4x4t::FromToMatrix(const aiVector3t& from, + const aiVector3t& to, aiMatrix4x4t& mtx) +{ + aiMatrix3x3t m3; + aiMatrix3x3t::FromToMatrix(from,to,m3); + mtx = aiMatrix4x4t(m3); + return mtx; +} + +#endif // __cplusplus +#endif // AI_MATRIX4X4_INL_INC diff --git a/Assimp/include/assimp/mesh.h b/Assimp/include/assimp/mesh.h new file mode 100644 index 0000000..c8648c7 --- /dev/null +++ b/Assimp/include/assimp/mesh.h @@ -0,0 +1,773 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 mesh.h + * @brief Declares the data structures in which the imported geometry is + returned by ASSIMP: aiMesh, aiFace and aiBone data structures. + */ +#pragma once +#ifndef AI_MESH_H_INC +#define AI_MESH_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +// Limits. These values are required to match the settings Assimp was +// compiled against. Therefore, do not redefine them unless you build the +// library from source using the same definitions. +// --------------------------------------------------------------------------- + +/** @def AI_MAX_FACE_INDICES + * Maximum number of indices per face (polygon). */ + +#ifndef AI_MAX_FACE_INDICES +# define AI_MAX_FACE_INDICES 0x7fff +#endif + +/** @def AI_MAX_BONE_WEIGHTS + * Maximum number of indices per face (polygon). */ + +#ifndef AI_MAX_BONE_WEIGHTS +# define AI_MAX_BONE_WEIGHTS 0x7fffffff +#endif + +/** @def AI_MAX_VERTICES + * Maximum number of vertices per mesh. */ + +#ifndef AI_MAX_VERTICES +# define AI_MAX_VERTICES 0x7fffffff +#endif + +/** @def AI_MAX_FACES + * Maximum number of faces per mesh. */ + +#ifndef AI_MAX_FACES +# define AI_MAX_FACES 0x7fffffff +#endif + +/** @def AI_MAX_NUMBER_OF_COLOR_SETS + * Supported number of vertex color sets per mesh. */ + +#ifndef AI_MAX_NUMBER_OF_COLOR_SETS +# define AI_MAX_NUMBER_OF_COLOR_SETS 0x8 +#endif // !! AI_MAX_NUMBER_OF_COLOR_SETS + +/** @def AI_MAX_NUMBER_OF_TEXTURECOORDS + * Supported number of texture coord sets (UV(W) channels) per mesh */ + +#ifndef AI_MAX_NUMBER_OF_TEXTURECOORDS +# define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x8 +#endif // !! AI_MAX_NUMBER_OF_TEXTURECOORDS + +// --------------------------------------------------------------------------- +/** @brief A single face in a mesh, referring to multiple vertices. + * + * If mNumIndices is 3, we call the face 'triangle', for mNumIndices > 3 + * it's called 'polygon' (hey, that's just a definition!). + *
+ * aiMesh::mPrimitiveTypes can be queried to quickly examine which types of + * primitive are actually present in a mesh. The #aiProcess_SortByPType flag + * executes a special post-processing algorithm which splits meshes with + * *different* primitive types mixed up (e.g. lines and triangles) in several + * 'clean' submeshes. Furthermore there is a configuration option ( + * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove + * specific kinds of primitives from the imported scene, completely and forever. + * In many cases you'll probably want to set this setting to + * @code + * aiPrimitiveType_LINE|aiPrimitiveType_POINT + * @endcode + * Together with the #aiProcess_Triangulate flag you can then be sure that + * #aiFace::mNumIndices is always 3. + * @note Take a look at the @link data Data Structures page @endlink for + * more information on the layout and winding order of a face. + */ +struct aiFace +{ + //! Number of indices defining this face. + //! The maximum value for this member is #AI_MAX_FACE_INDICES. + unsigned int mNumIndices; + + //! Pointer to the indices array. Size of the array is given in numIndices. + unsigned int* mIndices; + +#ifdef __cplusplus + + //! Default constructor + aiFace() + : mNumIndices( 0 ) + , mIndices( NULL ) + { + } + + //! Default destructor. Delete the index array + ~aiFace() + { + delete [] mIndices; + } + + //! Copy constructor. Copy the index array + aiFace( const aiFace& o) + : mIndices( NULL ) + { + *this = o; + } + + //! Assignment operator. Copy the index array + aiFace& operator = ( const aiFace& o) + { + if (&o == this) + return *this; + + delete[] mIndices; + mNumIndices = o.mNumIndices; + if (mNumIndices) { + mIndices = new unsigned int[mNumIndices]; + ::memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int)); + } + else { + mIndices = NULL; + } + return *this; + } + + //! Comparison operator. Checks whether the index array + //! of two faces is identical + bool operator== (const aiFace& o) const + { + if (mIndices == o.mIndices)return true; + else if (mIndices && mNumIndices == o.mNumIndices) + { + for (unsigned int i = 0;i < this->mNumIndices;++i) + if (mIndices[i] != o.mIndices[i])return false; + return true; + } + return false; + } + + //! Inverse comparison operator. Checks whether the index + //! array of two faces is NOT identical + bool operator != (const aiFace& o) const + { + return !(*this == o); + } +#endif // __cplusplus +}; // struct aiFace + + +// --------------------------------------------------------------------------- +/** @brief A single influence of a bone on a vertex. + */ +struct aiVertexWeight +{ + //! Index of the vertex which is influenced by the bone. + unsigned int mVertexId; + + //! The strength of the influence in the range (0...1). + //! The influence from all bones at one vertex amounts to 1. + float mWeight; + +#ifdef __cplusplus + + //! Default constructor + aiVertexWeight() { } + + //! Initialisation from a given index and vertex weight factor + //! \param pID ID + //! \param pWeight Vertex weight factor + aiVertexWeight( unsigned int pID, float pWeight) + : mVertexId( pID), mWeight( pWeight) + { /* nothing to do here */ } + +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** @brief A single bone of a mesh. + * + * A bone has a name by which it can be found in the frame hierarchy and by + * which it can be addressed by animations. In addition it has a number of + * influences on vertices. + */ +struct aiBone +{ + //! The name of the bone. + C_STRUCT aiString mName; + + //! The number of vertices affected by this bone + //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. + unsigned int mNumWeights; + + //! The vertices affected by this bone + C_STRUCT aiVertexWeight* mWeights; + + //! Matrix that transforms from mesh space to bone space in bind pose + C_STRUCT aiMatrix4x4 mOffsetMatrix; + +#ifdef __cplusplus + + //! Default constructor + aiBone() + : mName() + , mNumWeights( 0 ) + , mWeights( NULL ) + { + } + + //! Copy constructor + aiBone(const aiBone& other) + : mName( other.mName ) + , mNumWeights( other.mNumWeights ) + , mOffsetMatrix( other.mOffsetMatrix ) + { + if (other.mWeights && other.mNumWeights) + { + mWeights = new aiVertexWeight[mNumWeights]; + ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight)); + } + } + + //! Destructor - deletes the array of vertex weights + ~aiBone() + { + delete [] mWeights; + } +#endif // __cplusplus +}; + + +// --------------------------------------------------------------------------- +/** @brief Enumerates the types of geometric primitives supported by Assimp. + * + * @see aiFace Face data structure + * @see aiProcess_SortByPType Per-primitive sorting of meshes + * @see aiProcess_Triangulate Automatic triangulation + * @see AI_CONFIG_PP_SBP_REMOVE Removal of specific primitive types. + */ +enum aiPrimitiveType +{ + /** A point primitive. + * + * This is just a single vertex in the virtual world, + * #aiFace contains just one index for such a primitive. + */ + aiPrimitiveType_POINT = 0x1, + + /** A line primitive. + * + * This is a line defined through a start and an end position. + * #aiFace contains exactly two indices for such a primitive. + */ + aiPrimitiveType_LINE = 0x2, + + /** A triangular primitive. + * + * A triangle consists of three indices. + */ + aiPrimitiveType_TRIANGLE = 0x4, + + /** A higher-level polygon with more than 3 edges. + * + * A triangle is a polygon, but polygon in this context means + * "all polygons that are not triangles". The "Triangulate"-Step + * is provided for your convenience, it splits all polygons in + * triangles (which are much easier to handle). + */ + aiPrimitiveType_POLYGON = 0x8, + + + /** This value is not used. It is just here to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiPrimitiveType_Force32Bit = INT_MAX +#endif +}; //! enum aiPrimitiveType + +// Get the #aiPrimitiveType flag for a specific number of face indices +#define AI_PRIMITIVE_TYPE_FOR_N_INDICES(n) \ + ((n) > 3 ? aiPrimitiveType_POLYGON : (aiPrimitiveType)(1u << ((n)-1))) + + + +// --------------------------------------------------------------------------- +/** @brief NOT CURRENTLY IN USE. An AnimMesh is an attachment to an #aiMesh stores per-vertex + * animations for a particular frame. + * + * You may think of an #aiAnimMesh as a `patch` for the host mesh, which + * replaces only certain vertex data streams at a particular time. + * Each mesh stores n attached attached meshes (#aiMesh::mAnimMeshes). + * The actual relationship between the time line and anim meshes is + * established by #aiMeshAnim, which references singular mesh attachments + * by their ID and binds them to a time offset. +*/ +struct aiAnimMesh +{ + /** Replacement for aiMesh::mVertices. If this array is non-NULL, + * it *must* contain mNumVertices entries. The corresponding + * array in the host mesh must be non-NULL as well - animation + * meshes may neither add or nor remove vertex components (if + * a replacement array is NULL and the corresponding source + * array is not, the source data is taken instead)*/ + C_STRUCT aiVector3D* mVertices; + + /** Replacement for aiMesh::mNormals. */ + C_STRUCT aiVector3D* mNormals; + + /** Replacement for aiMesh::mTangents. */ + C_STRUCT aiVector3D* mTangents; + + /** Replacement for aiMesh::mBitangents. */ + C_STRUCT aiVector3D* mBitangents; + + /** Replacement for aiMesh::mColors */ + C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + /** Replacement for aiMesh::mTextureCoords */ + C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** The number of vertices in the aiAnimMesh, and thus the length of all + * the member arrays. + * + * This has always the same value as the mNumVertices property in the + * corresponding aiMesh. It is duplicated here merely to make the length + * of the member arrays accessible even if the aiMesh is not known, e.g. + * from language bindings. + */ + unsigned int mNumVertices; + + /** + * Weight of the AnimMesh. + */ + float mWeight; + +#ifdef __cplusplus + + aiAnimMesh() + : mVertices( NULL ) + , mNormals( NULL ) + , mTangents( NULL ) + , mBitangents( NULL ) + , mNumVertices( 0 ) + , mWeight( 0.0f ) + { + // fixme consider moving this to the ctor initializer list as well + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++){ + mTextureCoords[a] = NULL; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) { + mColors[a] = NULL; + } + } + + ~aiAnimMesh() + { + delete [] mVertices; + delete [] mNormals; + delete [] mTangents; + delete [] mBitangents; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) { + delete [] mTextureCoords[a]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) { + delete [] mColors[a]; + } + } + + /** Check whether the anim mesh overrides the vertex positions + * of its host mesh*/ + bool HasPositions() const { + return mVertices != NULL; + } + + /** Check whether the anim mesh overrides the vertex normals + * of its host mesh*/ + bool HasNormals() const { + return mNormals != NULL; + } + + /** Check whether the anim mesh overrides the vertex tangents + * and bitangents of its host mesh. As for aiMesh, + * tangents and bitangents always go together. */ + bool HasTangentsAndBitangents() const { + return mTangents != NULL; + } + + /** Check whether the anim mesh overrides a particular + * set of vertex colors on his host mesh. + * @param pIndex 0= AI_MAX_NUMBER_OF_COLOR_SETS ? false : mColors[pIndex] != NULL; + } + + /** Check whether the anim mesh overrides a particular + * set of texture coordinates on his host mesh. + * @param pIndex 0= AI_MAX_NUMBER_OF_TEXTURECOORDS ? false : mTextureCoords[pIndex] != NULL; + } + +#endif +}; + +// --------------------------------------------------------------------------- +/** @brief Enumerates the methods of mesh morphing supported by Assimp. + */ +enum aiMorphingMethod +{ + /** Interpolation between morph targets */ + aiMorphingMethod_VERTEX_BLEND = 0x1, + + /** Normalized morphing between morph targets */ + aiMorphingMethod_MORPH_NORMALIZED = 0x2, + + /** Relative morphing between morph targets */ + aiMorphingMethod_MORPH_RELATIVE = 0x3, + + /** This value is not used. It is just here to force the + * compiler to map this enum to a 32 Bit integer. + */ +#ifndef SWIG + _aiMorphingMethod_Force32Bit = INT_MAX +#endif +}; //! enum aiMorphingMethod + +// --------------------------------------------------------------------------- +/** @brief A mesh represents a geometry or model with a single material. +* +* It usually consists of a number of vertices and a series of primitives/faces +* referencing the vertices. In addition there might be a series of bones, each +* of them addressing a number of vertices with a certain weight. Vertex data +* is presented in channels with each channel containing a single per-vertex +* information such as a set of texture coords or a normal vector. +* If a data pointer is non-null, the corresponding data stream is present. +* From C++-programs you can also use the comfort functions Has*() to +* test for the presence of various data streams. +* +* A Mesh uses only a single material which is referenced by a material ID. +* @note The mPositions member is usually not optional. However, vertex positions +* *could* be missing if the #AI_SCENE_FLAGS_INCOMPLETE flag is set in +* @code +* aiScene::mFlags +* @endcode +*/ +struct aiMesh +{ + /** Bitwise combination of the members of the #aiPrimitiveType enum. + * This specifies which types of primitives are present in the mesh. + * The "SortByPrimitiveType"-Step can be used to make sure the + * output meshes consist of one primitive type each. + */ + unsigned int mPrimitiveTypes; + + /** The number of vertices in this mesh. + * This is also the size of all of the per-vertex data arrays. + * The maximum value for this member is #AI_MAX_VERTICES. + */ + unsigned int mNumVertices; + + /** The number of primitives (triangles, polygons, lines) in this mesh. + * This is also the size of the mFaces array. + * The maximum value for this member is #AI_MAX_FACES. + */ + unsigned int mNumFaces; + + /** Vertex positions. + * This array is always present in a mesh. The array is + * mNumVertices in size. + */ + C_STRUCT aiVector3D* mVertices; + + /** Vertex normals. + * The array contains normalized vectors, NULL if not present. + * The array is mNumVertices in size. Normals are undefined for + * point and line primitives. A mesh consisting of points and + * lines only may not have normal vectors. Meshes with mixed + * primitive types (i.e. lines and triangles) may have normals, + * but the normals for vertices that are only referenced by + * point or line primitives are undefined and set to QNaN (WARN: + * qNaN compares to inequal to *everything*, even to qNaN itself. + * Using code like this to check whether a field is qnan is: + * @code + * #define IS_QNAN(f) (f != f) + * @endcode + * still dangerous because even 1.f == 1.f could evaluate to false! ( + * remember the subtleties of IEEE754 artithmetics). Use stuff like + * @c fpclassify instead. + * @note Normal vectors computed by Assimp are always unit-length. + * However, this needn't apply for normals that have been taken + * directly from the model file. + */ + C_STRUCT aiVector3D* mNormals; + + /** Vertex tangents. + * The tangent of a vertex points in the direction of the positive + * X texture axis. The array contains normalized vectors, NULL if + * not present. The array is mNumVertices in size. A mesh consisting + * of points and lines only may not have normal vectors. Meshes with + * mixed primitive types (i.e. lines and triangles) may have + * normals, but the normals for vertices that are only referenced by + * point or line primitives are undefined and set to qNaN. See + * the #mNormals member for a detailed discussion of qNaNs. + * @note If the mesh contains tangents, it automatically also + * contains bitangents. + */ + C_STRUCT aiVector3D* mTangents; + + /** Vertex bitangents. + * The bitangent of a vertex points in the direction of the positive + * Y texture axis. The array contains normalized vectors, NULL if not + * present. The array is mNumVertices in size. + * @note If the mesh contains tangents, it automatically also contains + * bitangents. + */ + C_STRUCT aiVector3D* mBitangents; + + /** Vertex color sets. + * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex + * colors per vertex. NULL if not present. Each array is + * mNumVertices in size if present. + */ + C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; + + /** Vertex texture coords, also known as UV channels. + * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per + * vertex. NULL if not present. The array is mNumVertices in size. + */ + C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** Specifies the number of components for a given UV channel. + * Up to three channels are supported (UVW, for accessing volume + * or cube maps). If the value is 2 for a given channel n, the + * component p.z of mTextureCoords[n][p] is set to 0.0f. + * If the value is 1 for a given channel, p.y is set to 0.0f, too. + * @note 4D coords are not supported + */ + unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + /** The faces the mesh is constructed from. + * Each face refers to a number of vertices by their indices. + * This array is always present in a mesh, its size is given + * in mNumFaces. If the #AI_SCENE_FLAGS_NON_VERBOSE_FORMAT + * is NOT set each face references an unique set of vertices. + */ + C_STRUCT aiFace* mFaces; + + /** The number of bones this mesh contains. + * Can be 0, in which case the mBones array is NULL. + */ + unsigned int mNumBones; + + /** The bones of this mesh. + * A bone consists of a name by which it can be found in the + * frame hierarchy and a set of vertex weights. + */ + C_STRUCT aiBone** mBones; + + /** The material used by this mesh. + * A mesh uses only a single material. If an imported model uses + * multiple materials, the import splits up the mesh. Use this value + * as index into the scene's material list. + */ + unsigned int mMaterialIndex; + + /** Name of the mesh. Meshes can be named, but this is not a + * requirement and leaving this field empty is totally fine. + * There are mainly three uses for mesh names: + * - some formats name nodes and meshes independently. + * - importers tend to split meshes up to meet the + * one-material-per-mesh requirement. Assigning + * the same (dummy) name to each of the result meshes + * aids the caller at recovering the original mesh + * partitioning. + * - Vertex animations refer to meshes by their names. + **/ + C_STRUCT aiString mName; + + + /** The number of attachment meshes. Note! Currently only works with Collada loader. */ + unsigned int mNumAnimMeshes; + + /** Attachment meshes for this mesh, for vertex-based animation. + * Attachment meshes carry replacement data for some of the + * mesh'es vertex components (usually positions, normals). + * Note! Currently only works with Collada loader.*/ + C_STRUCT aiAnimMesh** mAnimMeshes; + + /** + * Method of morphing when animeshes are specified. + */ + unsigned int mMethod; + +#ifdef __cplusplus + + //! Default constructor. Initializes all members to 0 + aiMesh() + : mPrimitiveTypes( 0 ) + , mNumVertices( 0 ) + , mNumFaces( 0 ) + , mVertices( NULL ) + , mNormals( NULL ) + , mTangents( NULL ) + , mBitangents( NULL ) + , mFaces( NULL ) + , mNumBones( 0 ) + , mBones( NULL ) + , mMaterialIndex( 0 ) + , mNumAnimMeshes( 0 ) + , mAnimMeshes( NULL ) + , mMethod( 0 ) + { + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) + { + mNumUVComponents[a] = 0; + mTextureCoords[a] = NULL; + } + + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) + mColors[a] = NULL; + } + + //! Deletes all storage allocated for the mesh + ~aiMesh() + { + delete [] mVertices; + delete [] mNormals; + delete [] mTangents; + delete [] mBitangents; + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) { + delete [] mTextureCoords[a]; + } + for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) { + delete [] mColors[a]; + } + + // DO NOT REMOVE THIS ADDITIONAL CHECK + if (mNumBones && mBones) { + for( unsigned int a = 0; a < mNumBones; a++) { + delete mBones[a]; + } + delete [] mBones; + } + + if (mNumAnimMeshes && mAnimMeshes) { + for( unsigned int a = 0; a < mNumAnimMeshes; a++) { + delete mAnimMeshes[a]; + } + delete [] mAnimMeshes; + } + + delete [] mFaces; + } + + //! Check whether the mesh contains positions. Provided no special + //! scene flags are set, this will always be true + bool HasPositions() const + { return mVertices != NULL && mNumVertices > 0; } + + //! Check whether the mesh contains faces. If no special scene flags + //! are set this should always return true + bool HasFaces() const + { return mFaces != NULL && mNumFaces > 0; } + + //! Check whether the mesh contains normal vectors + bool HasNormals() const + { return mNormals != NULL && mNumVertices > 0; } + + //! Check whether the mesh contains tangent and bitangent vectors + //! It is not possible that it contains tangents and no bitangents + //! (or the other way round). The existence of one of them + //! implies that the second is there, too. + bool HasTangentsAndBitangents() const + { return mTangents != NULL && mBitangents != NULL && mNumVertices > 0; } + + //! Check whether the mesh contains a vertex color set + //! \param pIndex Index of the vertex color set + bool HasVertexColors( unsigned int pIndex) const + { + if( pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS) + return false; + else + return mColors[pIndex] != NULL && mNumVertices > 0; + } + + //! Check whether the mesh contains a texture coordinate set + //! \param pIndex Index of the texture coordinates set + bool HasTextureCoords( unsigned int pIndex) const + { + if( pIndex >= AI_MAX_NUMBER_OF_TEXTURECOORDS) + return false; + else + return mTextureCoords[pIndex] != NULL && mNumVertices > 0; + } + + //! Get the number of UV channels the mesh contains + unsigned int GetNumUVChannels() const + { + unsigned int n = 0; + while (n < AI_MAX_NUMBER_OF_TEXTURECOORDS && mTextureCoords[n])++n; + return n; + } + + //! Get the number of vertex color channels the mesh contains + unsigned int GetNumColorChannels() const + { + unsigned int n = 0; + while (n < AI_MAX_NUMBER_OF_COLOR_SETS && mColors[n])++n; + return n; + } + + //! Check whether the mesh contains bones + inline bool HasBones() const + { return mBones != NULL && mNumBones > 0; } + +#endif // __cplusplus +}; + +#ifdef __cplusplus +} +#endif //! extern "C" +#endif // AI_MESH_H_INC + diff --git a/Assimp/include/assimp/metadata.h b/Assimp/include/assimp/metadata.h new file mode 100644 index 0000000..92db9b5 --- /dev/null +++ b/Assimp/include/assimp/metadata.h @@ -0,0 +1,314 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 metadata.h + * @brief Defines the data structures for holding node meta information. + */ +#pragma once +#ifndef AI_METADATA_H_INC +#define AI_METADATA_H_INC + +#if defined(_MSC_VER) && (_MSC_VER <= 1500) +# include "Compiler/pstdint.h" +#else +# include +#endif + +// ------------------------------------------------------------------------------- +/** + * Enum used to distinguish data types + */ + // ------------------------------------------------------------------------------- +typedef enum aiMetadataType { + AI_BOOL = 0, + AI_INT32 = 1, + AI_UINT64 = 2, + AI_FLOAT = 3, + AI_DOUBLE = 4, + AI_AISTRING = 5, + AI_AIVECTOR3D = 6, + +#ifndef SWIG + FORCE_32BIT = INT_MAX +#endif +} aiMetadataType; + +// ------------------------------------------------------------------------------- +/** + * Metadata entry + * + * The type field uniquely identifies the underlying type of the data field + */ + // ------------------------------------------------------------------------------- +struct aiMetadataEntry { + aiMetadataType mType; + void* mData; +}; + +#ifdef __cplusplus + +#include + +// ------------------------------------------------------------------------------- +/** + * Helper functions to get the aiType enum entry for a type + */ + // ------------------------------------------------------------------------------- + +inline aiMetadataType GetAiType( bool ) { return AI_BOOL; } +inline aiMetadataType GetAiType( int32_t ) { return AI_INT32; } +inline aiMetadataType GetAiType( uint64_t ) { return AI_UINT64; } +inline aiMetadataType GetAiType( float ) { return AI_FLOAT; } +inline aiMetadataType GetAiType( double ) { return AI_DOUBLE; } +inline aiMetadataType GetAiType( const aiString & ) { return AI_AISTRING; } +inline aiMetadataType GetAiType( const aiVector3D & ) { return AI_AIVECTOR3D; } + +#endif // __cplusplus + +// ------------------------------------------------------------------------------- +/** + * Container for holding metadata. + * + * Metadata is a key-value store using string keys and values. + */ + // ------------------------------------------------------------------------------- +struct aiMetadata { + /** Length of the mKeys and mValues arrays, respectively */ + unsigned int mNumProperties; + + /** Arrays of keys, may not be NULL. Entries in this array may not be NULL as well. */ + C_STRUCT aiString* mKeys; + + /** Arrays of values, may not be NULL. Entries in this array may be NULL if the + * corresponding property key has no assigned value. */ + C_STRUCT aiMetadataEntry* mValues; + +#ifdef __cplusplus + + /** + * @brief The default constructor, set all members to zero by default. + */ + aiMetadata() + : mNumProperties(0) + , mKeys(NULL) + , mValues(NULL) { + // empty + } + + /** + * @brief The destructor. + */ + ~aiMetadata() { + delete [] mKeys; + mKeys = NULL; + if (mValues) { + // Delete each metadata entry + for (unsigned i=0; i(data); + break; + case AI_INT32: + delete static_cast(data); + break; + case AI_UINT64: + delete static_cast(data); + break; + case AI_FLOAT: + delete static_cast(data); + break; + case AI_DOUBLE: + delete static_cast(data); + break; + case AI_AISTRING: + delete static_cast(data); + break; + case AI_AIVECTOR3D: + delete static_cast(data); + break; +#ifndef SWIG + case FORCE_32BIT: +#endif + default: + break; + } + } + + // Delete the metadata array + delete [] mValues; + mValues = NULL; + } + } + + /** + * @brief Allocates property fields + keys. + * @param numProperties Number of requested properties. + */ + static inline + aiMetadata *Alloc( unsigned int numProperties ) { + if ( 0 == numProperties ) { + return nullptr; + } + + aiMetadata *data = new aiMetadata; + data->mNumProperties = numProperties; + data->mKeys = new aiString[ data->mNumProperties ](); + data->mValues = new aiMetadataEntry[ data->mNumProperties ](); + + return data; + } + + /** + * @brief Deallocates property fields + keys. + */ + static inline + void Dealloc( aiMetadata *metadata ) { + delete metadata; + } + + template + inline void Add(const std::string& key, const T& value) + { + aiString* new_keys = new aiString[mNumProperties + 1]; + aiMetadataEntry* new_values = new aiMetadataEntry[mNumProperties + 1]; + + for(unsigned int i = 0; i < mNumProperties; ++i) + { + new_keys[i] = mKeys[i]; + new_values[i] = mValues[i]; + } + + delete mKeys; + delete mValues; + + mKeys = new_keys; + mValues = new_values; + + mNumProperties++; + + Set(mNumProperties - 1, key, value); + } + + template + inline + bool Set( unsigned index, const std::string& key, const T& value ) { + // In range assertion + if ( index >= mNumProperties ) { + return false; + } + + // Ensure that we have a valid key. + if ( key.empty() ) { + return false; + } + + // Set metadata key + mKeys[index] = key; + + // Set metadata type + mValues[index].mType = GetAiType(value); + // Copy the given value to the dynamic storage + mValues[index].mData = new T(value); + + return true; + } + + template + inline + bool Get( unsigned index, T& value ) { + // In range assertion + if ( index >= mNumProperties ) { + return false; + } + + // Return false if the output data type does + // not match the found value's data type + if ( GetAiType( value ) != mValues[ index ].mType ) { + return false; + } + + // Otherwise, output the found value and + // return true + value = *static_cast(mValues[index].mData); + + return true; + } + + template + inline + bool Get( const aiString& key, T& value ) { + // Search for the given key + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + if ( mKeys[ i ] == key ) { + return Get( i, value ); + } + } + return false; + } + + template + inline bool Get( const std::string& key, T& value ) { + return Get(aiString(key), value); + } + + /// Return metadata entry for analyzing it by user. + /// \param [in] pIndex - index of the entry. + /// \param [out] pKey - pointer to the key value. + /// \param [out] pEntry - pointer to the entry: type and value. + /// \return false - if pIndex is out of range, else - true. + inline bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) { + if ( index >= mNumProperties ) { + return false; + } + + key = &mKeys[index]; + entry = &mValues[index]; + + return true; + } + +#endif // __cplusplus + +}; + +#endif // AI_METADATA_H_INC diff --git a/Assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h b/Assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h new file mode 100644 index 0000000..41d8004 --- /dev/null +++ b/Assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h @@ -0,0 +1,92 @@ +/* +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. + +---------------------------------------------------------------------- +*/ + +/** @file Android implementation of IOSystem using the standard C file functions. + * Aimed to ease the access to android assets */ + +#if __ANDROID__ and __ANDROID_API__ > 9 and defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) +#ifndef AI_ANDROIDJNIIOSYSTEM_H_INC +#define AI_ANDROIDJNIIOSYSTEM_H_INC + +#include +#include +#include +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Android extension to DefaultIOSystem using the standard C file functions */ +class ASSIMP_API AndroidJNIIOSystem : public DefaultIOSystem +{ +public: + + /** Initialize android activity data */ + std::string mApkWorkspacePath; + AAssetManager* mApkAssetManager; + + /** Constructor. */ + AndroidJNIIOSystem(ANativeActivity* activity); + + /** Destructor. */ + ~AndroidJNIIOSystem(); + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const; + + // ------------------------------------------------------------------- + /** Opens a file at the given path, with given mode */ + IOStream* Open( const char* strFile, const char* strMode); + + // ------------------------------------------------------------------------------------------------ + // Inits Android extractor + void AndroidActivityInit(ANativeActivity* activity); + + // ------------------------------------------------------------------------------------------------ + // Extracts android asset + bool AndroidExtractAsset(std::string name); + +}; + +} //!ns Assimp + +#endif //AI_ANDROIDJNIIOSYSTEM_H_INC +#endif //__ANDROID__ and __ANDROID_API__ > 9 and defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) diff --git a/Assimp/include/assimp/postprocess.h b/Assimp/include/assimp/postprocess.h new file mode 100644 index 0000000..970df8e --- /dev/null +++ b/Assimp/include/assimp/postprocess.h @@ -0,0 +1,645 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 postprocess.h + * @brief Definitions for import post processing steps + */ +#pragma once +#ifndef AI_POSTPROCESS_H_INC +#define AI_POSTPROCESS_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ----------------------------------------------------------------------------------- +/** @enum aiPostProcessSteps + * @brief Defines the flags for all possible post processing steps. + * + * @note Some steps are influenced by properties set on the Assimp::Importer itself + * + * @see Assimp::Importer::ReadFile() + * @see Assimp::Importer::SetPropertyInteger() + * @see aiImportFile + * @see aiImportFileEx + */ +// ----------------------------------------------------------------------------------- +enum aiPostProcessSteps +{ + + // ------------------------------------------------------------------------- + /**
Calculates the tangents and bitangents for the imported meshes. + * + * Does nothing if a mesh does not have normals. You might want this post + * processing step to be executed if you plan to use tangent space calculations + * such as normal mapping applied to the meshes. There's an importer property, + * #AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, which allows you to specify + * a maximum smoothing angle for the algorithm. However, usually you'll + * want to leave it at the default value. + */ + aiProcess_CalcTangentSpace = 0x1, + + // ------------------------------------------------------------------------- + /**
Identifies and joins identical vertex data sets within all + * imported meshes. + * + * After this step is run, each mesh contains unique vertices, + * so a vertex may be used by multiple faces. You usually want + * to use this post processing step. If your application deals with + * indexed geometry, this step is compulsory or you'll just waste rendering + * time. If this flag is not specified, no vertices are referenced by + * more than one face and no index buffer is required for rendering. + */ + aiProcess_JoinIdenticalVertices = 0x2, + + // ------------------------------------------------------------------------- + /**
Converts all the imported data to a left-handed coordinate space. + * + * By default the data is returned in a right-handed coordinate space (which + * OpenGL prefers). In this space, +X points to the right, + * +Z points towards the viewer, and +Y points upwards. In the DirectX + * coordinate space +X points to the right, +Y points upwards, and +Z points + * away from the viewer. + * + * You'll probably want to consider this flag if you use Direct3D for + * rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this + * setting and bundles all conversions typically required for D3D-based + * applications. + */ + aiProcess_MakeLeftHanded = 0x4, + + // ------------------------------------------------------------------------- + /**
Triangulates all faces of all meshes. + * + * By default the imported mesh data might contain faces with more than 3 + * indices. For rendering you'll usually want all faces to be triangles. + * This post processing step splits up faces with more than 3 indices into + * triangles. Line and point primitives are *not* modified! If you want + * 'triangles only' with no other kinds of primitives, try the following + * solution: + *
    + *
  • Specify both #aiProcess_Triangulate and #aiProcess_SortByPType
  • + *
  • Ignore all point and line meshes when you process assimp's output
  • + *
+ */ + aiProcess_Triangulate = 0x8, + + // ------------------------------------------------------------------------- + /**
Removes some parts of the data structure (animations, materials, + * light sources, cameras, textures, vertex components). + * + * The components to be removed are specified in a separate + * importer property, #AI_CONFIG_PP_RVC_FLAGS. This is quite useful + * if you don't need all parts of the output structure. Vertex colors + * are rarely used today for example... Calling this step to remove unneeded + * data from the pipeline as early as possible results in increased + * performance and a more optimized output data structure. + * This step is also useful if you want to force Assimp to recompute + * normals or tangents. The corresponding steps don't recompute them if + * they're already there (loaded from the source asset). By using this + * step you can make sure they are NOT there. + * + * This flag is a poor one, mainly because its purpose is usually + * misunderstood. Consider the following case: a 3D model has been exported + * from a CAD app, and it has per-face vertex colors. Vertex positions can't be + * shared, thus the #aiProcess_JoinIdenticalVertices step fails to + * optimize the data because of these nasty little vertex colors. + * Most apps don't even process them, so it's all for nothing. By using + * this step, unneeded components are excluded as early as possible + * thus opening more room for internal optimizations. + */ + aiProcess_RemoveComponent = 0x10, + + // ------------------------------------------------------------------------- + /**
Generates normals for all faces of all meshes. + * + * This is ignored if normals are already there at the time this flag + * is evaluated. Model importers try to load them from the source file, so + * they're usually already there. Face normals are shared between all points + * of a single face, so a single point can have multiple normals, which + * forces the library to duplicate vertices in some cases. + * #aiProcess_JoinIdenticalVertices is *senseless* then. + * + * This flag may not be specified together with #aiProcess_GenSmoothNormals. + */ + aiProcess_GenNormals = 0x20, + + // ------------------------------------------------------------------------- + /**
Generates smooth normals for all vertices in the mesh. + * + * This is ignored if normals are already there at the time this flag + * is evaluated. Model importers try to load them from the source file, so + * they're usually already there. + * + * This flag may not be specified together with + * #aiProcess_GenNormals. There's a importer property, + * #AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE which allows you to specify + * an angle maximum for the normal smoothing algorithm. Normals exceeding + * this limit are not smoothed, resulting in a 'hard' seam between two faces. + * Using a decent angle here (e.g. 80 degrees) results in very good visual + * appearance. + */ + aiProcess_GenSmoothNormals = 0x40, + + // ------------------------------------------------------------------------- + /**
Splits large meshes into smaller sub-meshes. + * + * This is quite useful for real-time rendering, where the number of triangles + * which can be maximally processed in a single draw-call is limited + * by the video driver/hardware. The maximum vertex buffer is usually limited + * too. Both requirements can be met with this step: you may specify both a + * triangle and vertex limit for a single mesh. + * + * The split limits can (and should!) be set through the + * #AI_CONFIG_PP_SLM_VERTEX_LIMIT and #AI_CONFIG_PP_SLM_TRIANGLE_LIMIT + * importer properties. The default values are #AI_SLM_DEFAULT_MAX_VERTICES and + * #AI_SLM_DEFAULT_MAX_TRIANGLES. + * + * Note that splitting is generally a time-consuming task, but only if there's + * something to split. The use of this step is recommended for most users. + */ + aiProcess_SplitLargeMeshes = 0x80, + + // ------------------------------------------------------------------------- + /**
Removes the node graph and pre-transforms all vertices with + * the local transformation matrices of their nodes. + * + * The output scene still contains nodes, however there is only a + * root node with children, each one referencing only one mesh, + * and each mesh referencing one material. For rendering, you can + * simply render all meshes in order - you don't need to pay + * attention to local transformations and the node hierarchy. + * Animations are removed during this step. + * This step is intended for applications without a scenegraph. + * The step CAN cause some problems: if e.g. a mesh of the asset + * contains normals and another, using the same material index, does not, + * they will be brought together, but the first meshes's part of + * the normal list is zeroed. However, these artifacts are rare. + * @note The #AI_CONFIG_PP_PTV_NORMALIZE configuration property + * can be set to normalize the scene's spatial dimension to the -1...1 + * range. + */ + aiProcess_PreTransformVertices = 0x100, + + // ------------------------------------------------------------------------- + /**
Limits the number of bones simultaneously affecting a single vertex + * to a maximum value. + * + * If any vertex is affected by more than the maximum number of bones, the least + * important vertex weights are removed and the remaining vertex weights are + * renormalized so that the weights still sum up to 1. + * The default bone weight limit is 4 (defined as #AI_LMW_MAX_WEIGHTS in + * config.h), but you can use the #AI_CONFIG_PP_LBW_MAX_WEIGHTS importer + * property to supply your own limit to the post processing step. + * + * If you intend to perform the skinning in hardware, this post processing + * step might be of interest to you. + */ + aiProcess_LimitBoneWeights = 0x200, + + // ------------------------------------------------------------------------- + /**
Validates the imported scene data structure. + * This makes sure that all indices are valid, all animations and + * bones are linked correctly, all material references are correct .. etc. + * + * It is recommended that you capture Assimp's log output if you use this flag, + * so you can easily find out what's wrong if a file fails the + * validation. The validator is quite strict and will find *all* + * inconsistencies in the data structure... It is recommended that plugin + * developers use it to debug their loaders. There are two types of + * validation failures: + *
    + *
  • Error: There's something wrong with the imported data. Further + * postprocessing is not possible and the data is not usable at all. + * The import fails. #Importer::GetErrorString() or #aiGetErrorString() + * carry the error message around.
  • + *
  • Warning: There are some minor issues (e.g. 1000000 animation + * keyframes with the same time), but further postprocessing and use + * of the data structure is still safe. Warning details are written + * to the log file, #AI_SCENE_FLAGS_VALIDATION_WARNING is set + * in #aiScene::mFlags
  • + *
+ * + * This post-processing step is not time-consuming. Its use is not + * compulsory, but recommended. + */ + aiProcess_ValidateDataStructure = 0x400, + + // ------------------------------------------------------------------------- + /**
Reorders triangles for better vertex cache locality. + * + * The step tries to improve the ACMR (average post-transform vertex cache + * miss ratio) for all meshes. The implementation runs in O(n) and is + * roughly based on the 'tipsify' algorithm (see this + * paper). + * + * If you intend to render huge models in hardware, this step might + * be of interest to you. The #AI_CONFIG_PP_ICL_PTCACHE_SIZE + * importer property can be used to fine-tune the cache optimization. + */ + aiProcess_ImproveCacheLocality = 0x800, + + // ------------------------------------------------------------------------- + /**
Searches for redundant/unreferenced materials and removes them. + * + * This is especially useful in combination with the + * #aiProcess_PreTransformVertices and #aiProcess_OptimizeMeshes flags. + * Both join small meshes with equal characteristics, but they can't do + * their work if two meshes have different materials. Because several + * material settings are lost during Assimp's import filters, + * (and because many exporters don't check for redundant materials), huge + * models often have materials which are are defined several times with + * exactly the same settings. + * + * Several material settings not contributing to the final appearance of + * a surface are ignored in all comparisons (e.g. the material name). + * So, if you're passing additional information through the + * content pipeline (probably using *magic* material names), don't + * specify this flag. Alternatively take a look at the + * #AI_CONFIG_PP_RRM_EXCLUDE_LIST importer property. + */ + aiProcess_RemoveRedundantMaterials = 0x1000, + + // ------------------------------------------------------------------------- + /**
This step tries to determine which meshes have normal vectors + * that are facing inwards and inverts them. + * + * The algorithm is simple but effective: + * the bounding box of all vertices + their normals is compared against + * the volume of the bounding box of all vertices without their normals. + * This works well for most objects, problems might occur with planar + * surfaces. However, the step tries to filter such cases. + * The step inverts all in-facing normals. Generally it is recommended + * to enable this step, although the result is not always correct. + */ + aiProcess_FixInfacingNormals = 0x2000, + + // ------------------------------------------------------------------------- + /**
This step splits meshes with more than one primitive type in + * homogeneous sub-meshes. + * + * The step is executed after the triangulation step. After the step + * returns, just one bit is set in aiMesh::mPrimitiveTypes. This is + * especially useful for real-time rendering where point and line + * primitives are often ignored or rendered separately. + * You can use the #AI_CONFIG_PP_SBP_REMOVE importer property to + * specify which primitive types you need. This can be used to easily + * exclude lines and points, which are rarely used, from the import. + */ + aiProcess_SortByPType = 0x8000, + + // ------------------------------------------------------------------------- + /**
This step searches all meshes for degenerate primitives and + * converts them to proper lines or points. + * + * A face is 'degenerate' if one or more of its points are identical. + * To have the degenerate stuff not only detected and collapsed but + * removed, try one of the following procedures: + *
1. (if you support lines and points for rendering but don't + * want the degenerates)
+ *
    + *
  • Specify the #aiProcess_FindDegenerates flag. + *
  • + *
  • Set the #AI_CONFIG_PP_FD_REMOVE importer property to + * 1. This will cause the step to remove degenerate triangles from the + * import as soon as they're detected. They won't pass any further + * pipeline steps. + *
  • + *
+ *
2.(if you don't support lines and points at all)
+ *
    + *
  • Specify the #aiProcess_FindDegenerates flag. + *
  • + *
  • Specify the #aiProcess_SortByPType flag. This moves line and + * point primitives to separate meshes. + *
  • + *
  • Set the #AI_CONFIG_PP_SBP_REMOVE importer property to + * @code aiPrimitiveType_POINTS | aiPrimitiveType_LINES + * @endcode to cause SortByPType to reject point + * and line meshes from the scene. + *
  • + *
+ * @note Degenerate polygons are not necessarily evil and that's why + * they're not removed by default. There are several file formats which + * don't support lines or points, and some exporters bypass the + * format specification and write them as degenerate triangles instead. + */ + aiProcess_FindDegenerates = 0x10000, + + // ------------------------------------------------------------------------- + /**
This step searches all meshes for invalid data, such as zeroed + * normal vectors or invalid UV coords and removes/fixes them. This is + * intended to get rid of some common exporter errors. + * + * This is especially useful for normals. If they are invalid, and + * the step recognizes this, they will be removed and can later + * be recomputed, i.e. by the #aiProcess_GenSmoothNormals flag.
+ * The step will also remove meshes that are infinitely small and reduce + * animation tracks consisting of hundreds if redundant keys to a single + * key. The AI_CONFIG_PP_FID_ANIM_ACCURACY config property decides + * the accuracy of the check for duplicate animation tracks. + */ + aiProcess_FindInvalidData = 0x20000, + + // ------------------------------------------------------------------------- + /**
This step converts non-UV mappings (such as spherical or + * cylindrical mapping) to proper texture coordinate channels. + * + * Most applications will support UV mapping only, so you will + * probably want to specify this step in every case. Note that Assimp is not + * always able to match the original mapping implementation of the + * 3D app which produced a model perfectly. It's always better to let the + * modelling app compute the UV channels - 3ds max, Maya, Blender, + * LightWave, and Modo do this for example. + * + * @note If this step is not requested, you'll need to process the + * #AI_MATKEY_MAPPING material property in order to display all assets + * properly. + */ + aiProcess_GenUVCoords = 0x40000, + + // ------------------------------------------------------------------------- + /**
This step applies per-texture UV transformations and bakes + * them into stand-alone vtexture coordinate channels. + * + * UV transformations are specified per-texture - see the + * #AI_MATKEY_UVTRANSFORM material key for more information. + * This step processes all textures with + * transformed input UV coordinates and generates a new (pre-transformed) UV channel + * which replaces the old channel. Most applications won't support UV + * transformations, so you will probably want to specify this step. + * + * @note UV transformations are usually implemented in real-time apps by + * transforming texture coordinates at vertex shader stage with a 3x3 + * (homogenous) transformation matrix. + */ + aiProcess_TransformUVCoords = 0x80000, + + // ------------------------------------------------------------------------- + /**
This step searches for duplicate meshes and replaces them + * with references to the first mesh. + * + * This step takes a while, so don't use it if speed is a concern. + * Its main purpose is to workaround the fact that many export + * file formats don't support instanced meshes, so exporters need to + * duplicate meshes. This step removes the duplicates again. Please + * note that Assimp does not currently support per-node material + * assignment to meshes, which means that identical meshes with + * different materials are currently *not* joined, although this is + * planned for future versions. + */ + aiProcess_FindInstances = 0x100000, + + // ------------------------------------------------------------------------- + /**
A postprocessing step to reduce the number of meshes. + * + * This will, in fact, reduce the number of draw calls. + * + * This is a very effective optimization and is recommended to be used + * together with #aiProcess_OptimizeGraph, if possible. The flag is fully + * compatible with both #aiProcess_SplitLargeMeshes and #aiProcess_SortByPType. + */ + aiProcess_OptimizeMeshes = 0x200000, + + + // ------------------------------------------------------------------------- + /**
A postprocessing step to optimize the scene hierarchy. + * + * Nodes without animations, bones, lights or cameras assigned are + * collapsed and joined. + * + * Node names can be lost during this step. If you use special 'tag nodes' + * to pass additional information through your content pipeline, use the + * #AI_CONFIG_PP_OG_EXCLUDE_LIST importer property to specify a + * list of node names you want to be kept. Nodes matching one of the names + * in this list won't be touched or modified. + * + * Use this flag with caution. Most simple files will be collapsed to a + * single node, so complex hierarchies are usually completely lost. This is not + * useful for editor environments, but probably a very effective + * optimization if you just want to get the model data, convert it to your + * own format, and render it as fast as possible. + * + * This flag is designed to be used with #aiProcess_OptimizeMeshes for best + * results. + * + * @note 'Crappy' scenes with thousands of extremely small meshes packed + * in deeply nested nodes exist for almost all file formats. + * #aiProcess_OptimizeMeshes in combination with #aiProcess_OptimizeGraph + * usually fixes them all and makes them renderable. + */ + aiProcess_OptimizeGraph = 0x400000, + + // ------------------------------------------------------------------------- + /**
This step flips all UV coordinates along the y-axis and adjusts + * material settings and bitangents accordingly. + * + * Output UV coordinate system: + * @code + * 0y|0y ---------- 1x|0y + * | | + * | | + * | | + * 0x|1y ---------- 1x|1y + * @endcode + * + * You'll probably want to consider this flag if you use Direct3D for + * rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this + * setting and bundles all conversions typically required for D3D-based + * applications. + */ + aiProcess_FlipUVs = 0x800000, + + // ------------------------------------------------------------------------- + /**
This step adjusts the output face winding order to be CW. + * + * The default face winding order is counter clockwise (CCW). + * + * Output face order: + * @code + * x2 + * + * x0 + * x1 + * @endcode + */ + aiProcess_FlipWindingOrder = 0x1000000, + + // ------------------------------------------------------------------------- + /**
This step splits meshes with many bones into sub-meshes so that each + * su-bmesh has fewer or as many bones as a given limit. + */ + aiProcess_SplitByBoneCount = 0x2000000, + + // ------------------------------------------------------------------------- + /**
This step removes bones losslessly or according to some threshold. + * + * In some cases (i.e. formats that require it) exporters are forced to + * assign dummy bone weights to otherwise static meshes assigned to + * animated meshes. Full, weight-based skinning is expensive while + * animating nodes is extremely cheap, so this step is offered to clean up + * the data in that regard. + * + * Use #AI_CONFIG_PP_DB_THRESHOLD to control this. + * Use #AI_CONFIG_PP_DB_ALL_OR_NONE if you want bones removed if and + * only if all bones within the scene qualify for removal. + */ + aiProcess_Debone = 0x4000000, + + // ------------------------------------------------------------------------- + /**
This step will perform a global scale of the model. + * + * Some importers are providing a mechanism to define a scaling unit for the + * model. This post processing step can be used to do so. + * + * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to control this. + */ + aiProcess_GlobalScale = 0x8000000 + + // aiProcess_GenEntityMeshes = 0x100000, + // aiProcess_OptimizeAnimations = 0x200000 + // aiProcess_FixTexturePaths = 0x200000 +}; + + +// --------------------------------------------------------------------------------------- +/** @def aiProcess_ConvertToLeftHanded + * @brief Shortcut flag for Direct3D-based applications. + * + * Supersedes the #aiProcess_MakeLeftHanded and #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder flags. + * The output data matches Direct3D's conventions: left-handed geometry, upper-left + * origin for UV coordinates and finally clockwise face order, suitable for CCW culling. + * + * @deprecated + */ +#define aiProcess_ConvertToLeftHanded ( \ + aiProcess_MakeLeftHanded | \ + aiProcess_FlipUVs | \ + aiProcess_FlipWindingOrder | \ + 0 ) + + +// --------------------------------------------------------------------------------------- +/** @def aiProcessPreset_TargetRealtime_Fast + * @brief Default postprocess configuration optimizing the data for real-time rendering. + * + * Applications would want to use this preset to load models on end-user PCs, + * maybe for direct use in game. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations + * in your application apply the #aiProcess_TransformUVCoords step, too. + * @note Please take the time to read the docs for the steps enabled by this preset. + * Some of them offer further configurable properties, while some of them might not be of + * use for you so it might be better to not specify them. + */ +#define aiProcessPreset_TargetRealtime_Fast ( \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + 0 ) + + // --------------------------------------------------------------------------------------- + /** @def aiProcessPreset_TargetRealtime_Quality + * @brief Default postprocess configuration optimizing the data for real-time rendering. + * + * Unlike #aiProcessPreset_TargetRealtime_Fast, this configuration + * performs some extra optimizations to improve rendering speed and + * to minimize memory usage. It could be a good choice for a level editor + * environment where import speed is not so important. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations + * in your application apply the #aiProcess_TransformUVCoords step, too. + * @note Please take the time to read the docs for the steps enabled by this preset. + * Some of them offer further configurable properties, while some of them might not be + * of use for you so it might be better to not specify them. + */ +#define aiProcessPreset_TargetRealtime_Quality ( \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenSmoothNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_ImproveCacheLocality | \ + aiProcess_LimitBoneWeights | \ + aiProcess_RemoveRedundantMaterials | \ + aiProcess_SplitLargeMeshes | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + aiProcess_FindDegenerates | \ + aiProcess_FindInvalidData | \ + 0 ) + + // --------------------------------------------------------------------------------------- + /** @def aiProcessPreset_TargetRealtime_MaxQuality + * @brief Default postprocess configuration optimizing the data for real-time rendering. + * + * This preset enables almost every optimization step to achieve perfectly + * optimized data. It's your choice for level editor environments where import speed + * is not important. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations + * in your application, apply the #aiProcess_TransformUVCoords step, too. + * @note Please take the time to read the docs for the steps enabled by this preset. + * Some of them offer further configurable properties, while some of them might not be + * of use for you so it might be better to not specify them. + */ +#define aiProcessPreset_TargetRealtime_MaxQuality ( \ + aiProcessPreset_TargetRealtime_Quality | \ + aiProcess_FindInstances | \ + aiProcess_ValidateDataStructure | \ + aiProcess_OptimizeMeshes | \ + 0 ) + + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // AI_POSTPROCESS_H_INC diff --git a/Assimp/include/assimp/quaternion.h b/Assimp/include/assimp/quaternion.h new file mode 100644 index 0000000..a5cb67a --- /dev/null +++ b/Assimp/include/assimp/quaternion.h @@ -0,0 +1,129 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 quaternion.h + * @brief Quaternion structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_QUATERNION_H_INC +#define AI_QUATERNION_H_INC + +#ifdef __cplusplus + +#include "defs.h" + +template class aiVector3t; +template class aiMatrix3x3t; + +// --------------------------------------------------------------------------- +/** Represents a quaternion in a 4D vector. */ +template +class aiQuaterniont +{ +public: + aiQuaterniont() : w(1.0), x(), y(), z() {} + aiQuaterniont(TReal pw, TReal px, TReal py, TReal pz) + : w(pw), x(px), y(py), z(pz) {} + + /** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */ + explicit aiQuaterniont( const aiMatrix3x3t& pRotMatrix); + + /** Construct from euler angles */ + aiQuaterniont( TReal rotx, TReal roty, TReal rotz); + + /** Construct from an axis-angle pair */ + aiQuaterniont( aiVector3t axis, TReal angle); + + /** Construct from a normalized quaternion stored in a vec3 */ + explicit aiQuaterniont( aiVector3t normalized); + + /** Returns a matrix representation of the quaternion */ + aiMatrix3x3t GetMatrix() const; + +public: + + bool operator== (const aiQuaterniont& o) const; + bool operator!= (const aiQuaterniont& o) const; + + bool Equal(const aiQuaterniont& o, TReal epsilon = 1e-6) const; + +public: + + /** Normalize the quaternion */ + aiQuaterniont& Normalize(); + + /** Compute quaternion conjugate */ + aiQuaterniont& Conjugate (); + + /** Rotate a point by this quaternion */ + aiVector3t Rotate (const aiVector3t& in); + + /** Multiply two quaternions */ + aiQuaterniont operator* (const aiQuaterniont& two) const; + +public: + + /** Performs a spherical interpolation between two quaternions and writes the result into the third. + * @param pOut Target object to received the interpolated rotation. + * @param pStart Start rotation of the interpolation at factor == 0. + * @param pEnd End rotation, factor == 1. + * @param pFactor Interpolation factor between 0 and 1. Values outside of this range yield undefined results. + */ + static void Interpolate( aiQuaterniont& pOut, const aiQuaterniont& pStart, + const aiQuaterniont& pEnd, TReal pFactor); + +public: + + //! w,x,y,z components of the quaternion + TReal w, x, y, z; +} ; + +typedef aiQuaterniont aiQuaternion; + +#else + +struct aiQuaternion { + ai_real w, x, y, z; +}; + +#endif + +#endif // AI_QUATERNION_H_INC diff --git a/Assimp/include/assimp/quaternion.inl b/Assimp/include/assimp/quaternion.inl new file mode 100644 index 0000000..d0bf583 --- /dev/null +++ b/Assimp/include/assimp/quaternion.inl @@ -0,0 +1,285 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 quaternion.inl + * @brief Inline implementation of aiQuaterniont operators + */ +#pragma once +#ifndef AI_QUATERNION_INL_INC +#define AI_QUATERNION_INL_INC + +#ifdef __cplusplus +#include "quaternion.h" + +#include + +// --------------------------------------------------------------------------- +template +bool aiQuaterniont::operator== (const aiQuaterniont& o) const +{ + return x == o.x && y == o.y && z == o.z && w == o.w; +} + +// --------------------------------------------------------------------------- +template +bool aiQuaterniont::operator!= (const aiQuaterniont& o) const +{ + return !(*this == o); +} + +// --------------------------------------------------------------------------- +template +inline bool aiQuaterniont::Equal(const aiQuaterniont& o, TReal epsilon) const { + return + std::abs(x - o.x) <= epsilon && + std::abs(y - o.y) <= epsilon && + std::abs(z - o.z) <= epsilon && + std::abs(w - o.w) <= epsilon; +} + +// --------------------------------------------------------------------------- +// Constructs a quaternion from a rotation matrix +template +inline aiQuaterniont::aiQuaterniont( const aiMatrix3x3t &pRotMatrix) +{ + TReal t = pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3; + + // large enough + if( t > static_cast(0)) + { + TReal s = std::sqrt(1 + t) * static_cast(2.0); + x = (pRotMatrix.c2 - pRotMatrix.b3) / s; + y = (pRotMatrix.a3 - pRotMatrix.c1) / s; + z = (pRotMatrix.b1 - pRotMatrix.a2) / s; + w = static_cast(0.25) * s; + } // else we have to check several cases + else if( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 ) + { + // Column 0: + TReal s = std::sqrt( static_cast(1.0) + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * static_cast(2.0); + x = static_cast(0.25) * s; + y = (pRotMatrix.b1 + pRotMatrix.a2) / s; + z = (pRotMatrix.a3 + pRotMatrix.c1) / s; + w = (pRotMatrix.c2 - pRotMatrix.b3) / s; + } + else if( pRotMatrix.b2 > pRotMatrix.c3) + { + // Column 1: + TReal s = std::sqrt( static_cast(1.0) + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * static_cast(2.0); + x = (pRotMatrix.b1 + pRotMatrix.a2) / s; + y = static_cast(0.25) * s; + z = (pRotMatrix.c2 + pRotMatrix.b3) / s; + w = (pRotMatrix.a3 - pRotMatrix.c1) / s; + } else + { + // Column 2: + TReal s = std::sqrt( static_cast(1.0) + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * static_cast(2.0); + x = (pRotMatrix.a3 + pRotMatrix.c1) / s; + y = (pRotMatrix.c2 + pRotMatrix.b3) / s; + z = static_cast(0.25) * s; + w = (pRotMatrix.b1 - pRotMatrix.a2) / s; + } +} + +// --------------------------------------------------------------------------- +// Construction from euler angles +template +inline aiQuaterniont::aiQuaterniont( TReal fPitch, TReal fYaw, TReal fRoll ) +{ + const TReal fSinPitch(std::sin(fPitch*static_cast(0.5))); + const TReal fCosPitch(std::cos(fPitch*static_cast(0.5))); + const TReal fSinYaw(std::sin(fYaw*static_cast(0.5))); + const TReal fCosYaw(std::cos(fYaw*static_cast(0.5))); + const TReal fSinRoll(std::sin(fRoll*static_cast(0.5))); + const TReal fCosRoll(std::cos(fRoll*static_cast(0.5))); + const TReal fCosPitchCosYaw(fCosPitch*fCosYaw); + const TReal fSinPitchSinYaw(fSinPitch*fSinYaw); + x = fSinRoll * fCosPitchCosYaw - fCosRoll * fSinPitchSinYaw; + y = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw; + z = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw; + w = fCosRoll * fCosPitchCosYaw + fSinRoll * fSinPitchSinYaw; +} + +// --------------------------------------------------------------------------- +// Returns a matrix representation of the quaternion +template +inline aiMatrix3x3t aiQuaterniont::GetMatrix() const +{ + aiMatrix3x3t resMatrix; + resMatrix.a1 = static_cast(1.0) - static_cast(2.0) * (y * y + z * z); + resMatrix.a2 = static_cast(2.0) * (x * y - z * w); + resMatrix.a3 = static_cast(2.0) * (x * z + y * w); + resMatrix.b1 = static_cast(2.0) * (x * y + z * w); + resMatrix.b2 = static_cast(1.0) - static_cast(2.0) * (x * x + z * z); + resMatrix.b3 = static_cast(2.0) * (y * z - x * w); + resMatrix.c1 = static_cast(2.0) * (x * z - y * w); + resMatrix.c2 = static_cast(2.0) * (y * z + x * w); + resMatrix.c3 = static_cast(1.0) - static_cast(2.0) * (x * x + y * y); + + return resMatrix; +} + +// --------------------------------------------------------------------------- +// Construction from an axis-angle pair +template +inline aiQuaterniont::aiQuaterniont( aiVector3t axis, TReal angle) +{ + axis.Normalize(); + + const TReal sin_a = std::sin( angle / 2 ); + const TReal cos_a = std::cos( angle / 2 ); + x = axis.x * sin_a; + y = axis.y * sin_a; + z = axis.z * sin_a; + w = cos_a; +} +// --------------------------------------------------------------------------- +// Construction from am existing, normalized quaternion +template +inline aiQuaterniont::aiQuaterniont( aiVector3t normalized) +{ + x = normalized.x; + y = normalized.y; + z = normalized.z; + + const TReal t = static_cast(1.0) - (x*x) - (y*y) - (z*z); + + if (t < static_cast(0.0)) { + w = static_cast(0.0); + } + else w = std::sqrt (t); +} + +// --------------------------------------------------------------------------- +// Performs a spherical interpolation between two quaternions +// Implementation adopted from the gmtl project. All others I found on the net fail in some cases. +// Congrats, gmtl! +template +inline void aiQuaterniont::Interpolate( aiQuaterniont& pOut, const aiQuaterniont& pStart, const aiQuaterniont& pEnd, TReal pFactor) +{ + // calc cosine theta + TReal cosom = pStart.x * pEnd.x + pStart.y * pEnd.y + pStart.z * pEnd.z + pStart.w * pEnd.w; + + // adjust signs (if necessary) + aiQuaterniont end = pEnd; + if( cosom < static_cast(0.0)) + { + cosom = -cosom; + end.x = -end.x; // Reverse all signs + end.y = -end.y; + end.z = -end.z; + end.w = -end.w; + } + + // Calculate coefficients + TReal sclp, sclq; + if( (static_cast(1.0) - cosom) > static_cast(0.0001)) // 0.0001 -> some epsillon + { + // Standard case (slerp) + TReal omega, sinom; + omega = std::acos( cosom); // extract theta from dot product's cos theta + sinom = std::sin( omega); + sclp = std::sin( (static_cast(1.0) - pFactor) * omega) / sinom; + sclq = std::sin( pFactor * omega) / sinom; + } else + { + // Very close, do linear interp (because it's faster) + sclp = static_cast(1.0) - pFactor; + sclq = pFactor; + } + + pOut.x = sclp * pStart.x + sclq * end.x; + pOut.y = sclp * pStart.y + sclq * end.y; + pOut.z = sclp * pStart.z + sclq * end.z; + pOut.w = sclp * pStart.w + sclq * end.w; +} + +// --------------------------------------------------------------------------- +template +inline aiQuaterniont& aiQuaterniont::Normalize() +{ + // compute the magnitude and divide through it + const TReal mag = std::sqrt(x*x + y*y + z*z + w*w); + if (mag) + { + const TReal invMag = static_cast(1.0)/mag; + x *= invMag; + y *= invMag; + z *= invMag; + w *= invMag; + } + return *this; +} + +// --------------------------------------------------------------------------- +template +inline aiQuaterniont aiQuaterniont::operator* (const aiQuaterniont& t) const +{ + return aiQuaterniont(w*t.w - x*t.x - y*t.y - z*t.z, + w*t.x + x*t.w + y*t.z - z*t.y, + w*t.y + y*t.w + z*t.x - x*t.z, + w*t.z + z*t.w + x*t.y - y*t.x); +} + +// --------------------------------------------------------------------------- +template +inline aiQuaterniont& aiQuaterniont::Conjugate () +{ + x = -x; + y = -y; + z = -z; + return *this; +} + +// --------------------------------------------------------------------------- +template +inline aiVector3t aiQuaterniont::Rotate (const aiVector3t& v) +{ + aiQuaterniont q2(0.f,v.x,v.y,v.z), q = *this, qinv = q; + qinv.Conjugate(); + + q = q*q2*qinv; + return aiVector3t(q.x,q.y,q.z); +} + +#endif +#endif // AI_QUATERNION_INL_INC diff --git a/Assimp/include/assimp/scene.h b/Assimp/include/assimp/scene.h new file mode 100644 index 0000000..342c316 --- /dev/null +++ b/Assimp/include/assimp/scene.h @@ -0,0 +1,384 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 scene.h + * @brief Defines the data structures in which the imported scene is returned. + */ +#pragma once +#ifndef AI_SCENE_H_INC +#define AI_SCENE_H_INC + +#include "types.h" +#include "texture.h" +#include "mesh.h" +#include "light.h" +#include "camera.h" +#include "material.h" +#include "anim.h" +#include "metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" +#endif + +// ------------------------------------------------------------------------------- +/** + * A node in the imported hierarchy. + * + * Each node has name, a parent node (except for the root node), + * a transformation relative to its parent and possibly several child nodes. + * Simple file formats don't support hierarchical structures - for these formats + * the imported scene does consist of only a single root node without children. + */ +// ------------------------------------------------------------------------------- +struct ASSIMP_API aiNode +{ + /** The name of the node. + * + * The name might be empty (length of zero) but all nodes which + * need to be referenced by either bones or animations are named. + * Multiple nodes may have the same name, except for nodes which are referenced + * by bones (see #aiBone and #aiMesh::mBones). Their names *must* be unique. + * + * Cameras and lights reference a specific node by name - if there + * are multiple nodes with this name, they are assigned to each of them. + *
+ * There are no limitations with regard to the characters contained in + * the name string as it is usually taken directly from the source file. + * + * Implementations should be able to handle tokens such as whitespace, tabs, + * line feeds, quotation marks, ampersands etc. + * + * Sometimes assimp introduces new nodes not present in the source file + * into the hierarchy (usually out of necessity because sometimes the + * source hierarchy format is simply not compatible). Their names are + * surrounded by @verbatim <> @endverbatim e.g. + * @verbatim @endverbatim. + */ + C_STRUCT aiString mName; + + /** The transformation relative to the node's parent. */ + C_STRUCT aiMatrix4x4 mTransformation; + + /** Parent node. NULL if this node is the root node. */ + C_STRUCT aiNode* mParent; + + /** The number of child nodes of this node. */ + unsigned int mNumChildren; + + /** The child nodes of this node. NULL if mNumChildren is 0. */ + C_STRUCT aiNode** mChildren; + + /** The number of meshes of this node. */ + unsigned int mNumMeshes; + + /** The meshes of this node. Each entry is an index into the + * mesh list of the #aiScene. + */ + unsigned int* mMeshes; + + /** Metadata associated with this node or NULL if there is no metadata. + * Whether any metadata is generated depends on the source file format. See the + * @link importer_notes @endlink page for more information on every source file + * format. Importers that don't document any metadata don't write any. + */ + C_STRUCT aiMetadata* mMetaData; + +#ifdef __cplusplus + /** Constructor */ + aiNode(); + + /** Construction from a specific name */ + explicit aiNode(const std::string& name); + + /** Destructor */ + ~aiNode(); + + /** Searches for a node with a specific name, beginning at this + * nodes. Normally you will call this method on the root node + * of the scene. + * + * @param name Name to search for + * @return NULL or a valid Node if the search was successful. + */ + inline + const aiNode* FindNode(const aiString& name) const { + return FindNode(name.data); + } + + inline + aiNode* FindNode(const aiString& name) { + return FindNode(name.data); + } + + const aiNode* FindNode(const char* name) const; + + aiNode* FindNode(const char* name); + + /** + * @brief Will add new children. + * @param numChildren Number of children to add. + * @param children The array with pointers showing to the children. + */ + void addChildren(unsigned int numChildren, aiNode **children); +#endif // __cplusplus +}; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +// ------------------------------------------------------------------------------- +/** + * Specifies that the scene data structure that was imported is not complete. + * This flag bypasses some internal validations and allows the import + * of animation skeletons, material libraries or camera animation paths + * using Assimp. Most applications won't support such data. + */ +#define AI_SCENE_FLAGS_INCOMPLETE 0x1 + +/** + * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) + * if the validation is successful. In a validated scene you can be sure that + * any cross references in the data structure (e.g. vertex indices) are valid. + */ +#define AI_SCENE_FLAGS_VALIDATED 0x2 + +/** + * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) + * if the validation is successful but some issues have been found. + * This can for example mean that a texture that does not exist is referenced + * by a material or that the bone weights for a vertex don't sum to 1.0 ... . + * In most cases you should still be able to use the import. This flag could + * be useful for applications which don't capture Assimp's log output. + */ +#define AI_SCENE_FLAGS_VALIDATION_WARNING 0x4 + +/** + * This flag is currently only set by the aiProcess_JoinIdenticalVertices step. + * It indicates that the vertices of the output meshes aren't in the internal + * verbose format anymore. In the verbose format all vertices are unique, + * no vertex is ever referenced by more than one face. + */ +#define AI_SCENE_FLAGS_NON_VERBOSE_FORMAT 0x8 + + /** + * Denotes pure height-map terrain data. Pure terrains usually consist of quads, + * sometimes triangles, in a regular grid. The x,y coordinates of all vertex + * positions refer to the x,y coordinates on the terrain height map, the z-axis + * stores the elevation at a specific point. + * + * TER (Terragen) and HMP (3D Game Studio) are height map formats. + * @note Assimp is probably not the best choice for loading *huge* terrains - + * fully triangulated data takes extremely much free store and should be avoided + * as long as possible (typically you'll do the triangulation when you actually + * need to render it). + */ +#define AI_SCENE_FLAGS_TERRAIN 0x10 + + /** + * Specifies that the scene data can be shared between structures. For example: + * one vertex in few faces. \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT can not be + * used for this because \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT has internal + * meaning about postprocessing steps. + */ +#define AI_SCENE_FLAGS_ALLOW_SHARED 0x20 + +// ------------------------------------------------------------------------------- +/** The root structure of the imported data. + * + * Everything that was imported from the given file can be accessed from here. + * Objects of this class are generally maintained and owned by Assimp, not + * by the caller. You shouldn't want to instance it, nor should you ever try to + * delete a given scene on your own. + */ +// ------------------------------------------------------------------------------- +struct aiScene +{ + /** Any combination of the AI_SCENE_FLAGS_XXX flags. By default + * this value is 0, no flags are set. Most applications will + * want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE + * bit set. + */ + unsigned int mFlags; + + /** The root node of the hierarchy. + * + * There will always be at least the root node if the import + * was successful (and no special flags have been set). + * Presence of further nodes depends on the format and content + * of the imported file. + */ + C_STRUCT aiNode* mRootNode; + + /** The number of meshes in the scene. */ + unsigned int mNumMeshes; + + /** The array of meshes. + * + * Use the indices given in the aiNode structure to access + * this array. The array is mNumMeshes in size. If the + * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always + * be at least ONE material. + */ + C_STRUCT aiMesh** mMeshes; + + /** The number of materials in the scene. */ + unsigned int mNumMaterials; + + /** The array of materials. + * + * Use the index given in each aiMesh structure to access this + * array. The array is mNumMaterials in size. If the + * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always + * be at least ONE material. + */ + C_STRUCT aiMaterial** mMaterials; + + /** The number of animations in the scene. */ + unsigned int mNumAnimations; + + /** The array of animations. + * + * All animations imported from the given file are listed here. + * The array is mNumAnimations in size. + */ + C_STRUCT aiAnimation** mAnimations; + + /** The number of textures embedded into the file */ + unsigned int mNumTextures; + + /** The array of embedded textures. + * + * Not many file formats embed their textures into the file. + * An example is Quake's MDL format (which is also used by + * some GameStudio versions) + */ + C_STRUCT aiTexture** mTextures; + + /** The number of light sources in the scene. Light sources + * are fully optional, in most cases this attribute will be 0 + */ + unsigned int mNumLights; + + /** The array of light sources. + * + * All light sources imported from the given file are + * listed here. The array is mNumLights in size. + */ + C_STRUCT aiLight** mLights; + + /** The number of cameras in the scene. Cameras + * are fully optional, in most cases this attribute will be 0 + */ + unsigned int mNumCameras; + + /** The array of cameras. + * + * All cameras imported from the given file are listed here. + * The array is mNumCameras in size. The first camera in the + * array (if existing) is the default camera view into + * the scene. + */ + C_STRUCT aiCamera** mCameras; + +#ifdef __cplusplus + + //! Default constructor - set everything to 0/NULL + ASSIMP_API aiScene(); + + //! Destructor + ASSIMP_API ~aiScene(); + + //! Check whether the scene contains meshes + //! Unless no special scene flags are set this will always be true. + inline bool HasMeshes() const { + return mMeshes != NULL && mNumMeshes > 0; + } + + //! Check whether the scene contains materials + //! Unless no special scene flags are set this will always be true. + inline bool HasMaterials() const { + return mMaterials != NULL && mNumMaterials > 0; + } + + //! Check whether the scene contains lights + inline bool HasLights() const { + return mLights != NULL && mNumLights > 0; + } + + //! Check whether the scene contains textures + inline bool HasTextures() const { + return mTextures != NULL && mNumTextures > 0; + } + + //! Check whether the scene contains cameras + inline bool HasCameras() const { + return mCameras != NULL && mNumCameras > 0; + } + + //! Check whether the scene contains animations + inline bool HasAnimations() const { + return mAnimations != NULL && mNumAnimations > 0; + } + +#endif // __cplusplus + + /** Internal data, do not touch */ +#ifdef __cplusplus + void* mPrivate; +#else + char* mPrivate; +#endif + +}; + +#ifdef __cplusplus +} //! namespace Assimp +#endif + +#endif // AI_SCENE_H_INC diff --git a/Assimp/include/assimp/texture.h b/Assimp/include/assimp/texture.h new file mode 100644 index 0000000..c09ef2c --- /dev/null +++ b/Assimp/include/assimp/texture.h @@ -0,0 +1,217 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 texture.h + * @brief Defines texture helper structures for the library + * + * Used for file formats which embed their textures into the model file. + * Supported are both normal textures, which are stored as uncompressed + * pixels, and "compressed" textures, which are stored in a file format + * such as PNG or TGA. + */ +#pragma once +#ifndef AI_TEXTURE_H_INC +#define AI_TEXTURE_H_INC + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// -------------------------------------------------------------------------------- + +/** \def AI_EMBEDDED_TEXNAME_PREFIX + * \ref AI_MAKE_EMBEDDED_TEXNAME + */ +#ifndef AI_EMBEDDED_TEXNAME_PREFIX +# define AI_EMBEDDED_TEXNAME_PREFIX "*" +#endif + +/** @def AI_MAKE_EMBEDDED_TEXNAME + * Used to build the reserved path name used by the material system to + * reference textures that are embedded into their corresponding + * model files. The parameter specifies the index of the texture + * (zero-based, in the aiScene::mTextures array) + */ +#if (!defined AI_MAKE_EMBEDDED_TEXNAME) +# define AI_MAKE_EMBEDDED_TEXNAME(_n_) AI_EMBEDDED_TEXNAME_PREFIX # _n_ +#endif + + +#include "./Compiler/pushpack1.h" + +// -------------------------------------------------------------------------------- +/** @brief Helper structure to represent a texel in a ARGB8888 format +* +* Used by aiTexture. +*/ +struct aiTexel +{ + unsigned char b,g,r,a; + +#ifdef __cplusplus + //! Comparison operator + bool operator== (const aiTexel& other) const + { + return b == other.b && r == other.r && + g == other.g && a == other.a; + } + + //! Inverse comparison operator + bool operator!= (const aiTexel& other) const + { + return b != other.b || r != other.r || + g != other.g || a != other.a; + } + + //! Conversion to a floating-point 4d color + operator aiColor4D() const + { + return aiColor4D(r/255.f,g/255.f,b/255.f,a/255.f); + } +#endif // __cplusplus + +} PACK_STRUCT; + +#include "./Compiler/poppack1.h" + +// -------------------------------------------------------------------------------- +/** Helper structure to describe an embedded texture + * + * Normally textures are contained in external files but some file formats embed + * them directly in the model file. There are two types of embedded textures: + * 1. Uncompressed textures. The color data is given in an uncompressed format. + * 2. Compressed textures stored in a file format like png or jpg. The raw file + * bytes are given so the application must utilize an image decoder (e.g. DevIL) to + * get access to the actual color data. + * + * Embedded textures are referenced from materials using strings like "*0", "*1", etc. + * as the texture paths (a single asterisk character followed by the + * zero-based index of the texture in the aiScene::mTextures array). + */ +struct aiTexture +{ + /** Width of the texture, in pixels + * + * If mHeight is zero the texture is compressed in a format + * like JPEG. In this case mWidth specifies the size of the + * memory area pcData is pointing to, in bytes. + */ + unsigned int mWidth; + + /** Height of the texture, in pixels + * + * If this value is zero, pcData points to an compressed texture + * in any format (e.g. JPEG). + */ + unsigned int mHeight; + + /** A hint from the loader to make it easier for applications + * to determine the type of embedded textures. + * + * If mHeight != 0 this member is show how data is packed. Hint will consist of + * two parts: channel order and channel bitness (count of the bits for every + * color channel). For simple parsing by the viewer it's better to not omit + * absent color channel and just use 0 for bitness. For example: + * 1. Image contain RGBA and 8 bit per channel, achFormatHint == "rgba8888"; + * 2. Image contain ARGB and 8 bit per channel, achFormatHint == "argb8888"; + * 3. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, achFormatHint == "rgba5650"; + * 4. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010"; + * If mHeight == 0 then achFormatHint is set set to '\\0\\0\\0\\0' if the loader has no additional + * information about the texture file format used OR the + * file extension of the format without a trailing dot. If there + * are multiple file extensions for a format, the shortest + * extension is chosen (JPEG maps to 'jpg', not to 'jpeg'). + * E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case. + * The fourth character will always be '\\0'. + */ + char achFormatHint[9];// 8 for string + 1 for terminator. + + /** Data of the texture. + * + * Points to an array of mWidth * mHeight aiTexel's. + * The format of the texture data is always ARGB8888 to + * make the implementation for user of the library as easy + * as possible. If mHeight = 0 this is a pointer to a memory + * buffer of size mWidth containing the compressed texture + * data. Good luck, have fun! + */ + C_STRUCT aiTexel* pcData; + +#ifdef __cplusplus + + //! For compressed textures (mHeight == 0): compare the + //! format hint against a given string. + //! @param s Input string. 3 characters are maximally processed. + //! Example values: "jpg", "png" + //! @return true if the given string matches the format hint + bool CheckFormat(const char* s) const + { + return (0 == ::strncmp(achFormatHint, s, sizeof(achFormatHint))); + } + + // Construction + aiTexture () + : mWidth (0) + , mHeight (0) + , pcData (NULL) + { + achFormatHint[0] = achFormatHint[1] = 0; + achFormatHint[2] = achFormatHint[3] = 0; + } + + // Destruction + ~aiTexture () + { + delete[] pcData; + } +#endif +}; + + +#ifdef __cplusplus +} +#endif + +#endif // AI_TEXTURE_H_INC diff --git a/Assimp/include/assimp/types.h b/Assimp/include/assimp/types.h new file mode 100644 index 0000000..0012a0b --- /dev/null +++ b/Assimp/include/assimp/types.h @@ -0,0 +1,518 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 types.h + * Basic data types and primitives, such as vectors or colors. + */ +#pragma once +#ifndef AI_TYPES_H_INC +#define AI_TYPES_H_INC + +// Some runtime headers +#include +#include +#include +#include + +// Our compile configuration +#include "defs.h" + +// Some types moved to separate header due to size of operators +#include "vector3.h" +#include "vector2.h" +#include "color4.h" +#include "matrix3x3.h" +#include "matrix4x4.h" +#include "quaternion.h" + +#ifdef __cplusplus +#include +#include // for std::nothrow_t +#include // for aiString::Set(const std::string&) + +namespace Assimp { + //! @cond never +namespace Intern { + // -------------------------------------------------------------------- + /** @brief Internal helper class to utilize our internal new/delete + * routines for allocating object of this and derived classes. + * + * By doing this you can safely share class objects between Assimp + * and the application - it works even over DLL boundaries. A good + * example is the #IOSystem where the application allocates its custom + * #IOSystem, then calls #Importer::SetIOSystem(). When the Importer + * destructs, Assimp calls operator delete on the stored #IOSystem. + * If it lies on a different heap than Assimp is working with, + * the application is determined to crash. + */ + // -------------------------------------------------------------------- +#ifndef SWIG + struct ASSIMP_API AllocateFromAssimpHeap { + // http://www.gotw.ca/publications/mill15.htm + + // new/delete overload + void *operator new ( size_t num_bytes) /* throw( std::bad_alloc ) */; + void *operator new ( size_t num_bytes, const std::nothrow_t& ) throw(); + void operator delete ( void* data); + + // array new/delete overload + void *operator new[] ( size_t num_bytes) /* throw( std::bad_alloc ) */; + void *operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw(); + void operator delete[] ( void* data); + + }; // struct AllocateFromAssimpHeap +#endif +} // namespace Intern + //! @endcond +} // namespace Assimp + +extern "C" { +#endif + +/** Maximum dimension for strings, ASSIMP strings are zero terminated. */ +#ifdef __cplusplus +static +const size_t MAXLEN = 1024; +#else +# define MAXLEN 1024 +#endif + +// ---------------------------------------------------------------------------------- +/** Represents a plane in a three-dimensional, euclidean space +*/ +struct aiPlane +{ +#ifdef __cplusplus + aiPlane () : a(0.f), b(0.f), c(0.f), d(0.f) {} + aiPlane (ai_real _a, ai_real _b, ai_real _c, ai_real _d) + : a(_a), b(_b), c(_c), d(_d) {} + + aiPlane (const aiPlane& o) : a(o.a), b(o.b), c(o.c), d(o.d) {} + +#endif // !__cplusplus + + //! Plane equation + ai_real a,b,c,d; +}; // !struct aiPlane + +// ---------------------------------------------------------------------------------- +/** Represents a ray +*/ +struct aiRay +{ +#ifdef __cplusplus + aiRay () {} + aiRay (const aiVector3D& _pos, const aiVector3D& _dir) + : pos(_pos), dir(_dir) {} + + aiRay (const aiRay& o) : pos (o.pos), dir (o.dir) {} + +#endif // !__cplusplus + + //! Position and direction of the ray + C_STRUCT aiVector3D pos, dir; +}; // !struct aiRay + +// ---------------------------------------------------------------------------------- +/** Represents a color in Red-Green-Blue space. +*/ +struct aiColor3D +{ +#ifdef __cplusplus + aiColor3D () : r(0.0f), g(0.0f), b(0.0f) {} + aiColor3D (ai_real _r, ai_real _g, ai_real _b) : r(_r), g(_g), b(_b) {} + explicit aiColor3D (ai_real _r) : r(_r), g(_r), b(_r) {} + aiColor3D (const aiColor3D& o) : r(o.r), g(o.g), b(o.b) {} + + /** Component-wise comparison */ + // TODO: add epsilon? + bool operator == (const aiColor3D& other) const + {return r == other.r && g == other.g && b == other.b;} + + /** Component-wise inverse comparison */ + // TODO: add epsilon? + bool operator != (const aiColor3D& other) const + {return r != other.r || g != other.g || b != other.b;} + + /** Component-wise comparison */ + // TODO: add epsilon? + bool operator < (const aiColor3D& other) const { + return r < other.r || ( r == other.r && (g < other.g || (g == other.g && b < other.b ) ) ); + } + + /** Component-wise addition */ + aiColor3D operator+(const aiColor3D& c) const { + return aiColor3D(r+c.r,g+c.g,b+c.b); + } + + /** Component-wise subtraction */ + aiColor3D operator-(const aiColor3D& c) const { + return aiColor3D(r-c.r,g-c.g,b-c.b); + } + + /** Component-wise multiplication */ + aiColor3D operator*(const aiColor3D& c) const { + return aiColor3D(r*c.r,g*c.g,b*c.b); + } + + /** Multiply with a scalar */ + aiColor3D operator*(ai_real f) const { + return aiColor3D(r*f,g*f,b*f); + } + + /** Access a specific color component */ + ai_real operator[](unsigned int i) const { + return *(&r + i); + } + + /** Access a specific color component */ + ai_real& operator[](unsigned int i) { + if ( 0 == i ) { + return r; + } else if ( 1 == i ) { + return g; + } else if ( 2 == i ) { + return b; + } + return r; + } + + /** Check whether a color is black */ + bool IsBlack() const { + static const ai_real epsilon = ai_real(10e-3); + return std::fabs( r ) < epsilon && std::fabs( g ) < epsilon && std::fabs( b ) < epsilon; + } + +#endif // !__cplusplus + + //! Red, green and blue color values + ai_real r, g, b; +}; // !struct aiColor3D + +// ---------------------------------------------------------------------------------- +/** Represents an UTF-8 string, zero byte terminated. + * + * The character set of an aiString is explicitly defined to be UTF-8. This Unicode + * transformation was chosen in the belief that most strings in 3d files are limited + * to ASCII, thus the character set needed to be strictly ASCII compatible. + * + * Most text file loaders provide proper Unicode input file handling, special unicode + * characters are correctly transcoded to UTF8 and are kept throughout the libraries' + * import pipeline. + * + * For most applications, it will be absolutely sufficient to interpret the + * aiString as ASCII data and work with it as one would work with a plain char*. + * Windows users in need of proper support for i.e asian characters can use the + * MultiByteToWideChar(), WideCharToMultiByte() WinAPI functionality to convert the + * UTF-8 strings to their working character set (i.e. MBCS, WideChar). + * + * We use this representation instead of std::string to be C-compatible. The + * (binary) length of such a string is limited to MAXLEN characters (including the + * the terminating zero). +*/ +struct aiString +{ +#ifdef __cplusplus + /** Default constructor, the string is set to have zero length */ + aiString() : + length(0) + { + data[0] = '\0'; + +#ifdef ASSIMP_BUILD_DEBUG + // Debug build: overwrite the string on its full length with ESC (27) + memset(data+1,27,MAXLEN-1); +#endif + } + + /** Copy constructor */ + aiString(const aiString& rOther) : + length(rOther.length) + { + // Crop the string to the maximum length + length = length>=MAXLEN?MAXLEN-1:length; + memcpy( data, rOther.data, length); + data[length] = '\0'; + } + + /** Constructor from std::string */ + explicit aiString(const std::string& pString) : + length(pString.length()) + { + length = length>=MAXLEN?MAXLEN-1:length; + memcpy( data, pString.c_str(), length); + data[length] = '\0'; + } + + /** Copy a std::string to the aiString */ + void Set( const std::string& pString) { + if( pString.length() > MAXLEN - 1) { + return; + } + length = pString.length(); + memcpy( data, pString.c_str(), length); + data[length] = 0; + } + + /** Copy a const char* to the aiString */ + void Set( const char* sz) { + const size_t len = ::strlen(sz); + if( len > MAXLEN - 1) { + return; + } + length = len; + memcpy( data, sz, len); + data[len] = 0; + } + + /** Assign a const char* to the string */ + aiString& operator = (const char* sz) { + Set(sz); + return *this; + } + + /** Assign a cstd::string to the string */ + aiString& operator = ( const std::string& pString) { + Set(pString); + return *this; + } + + /** Comparison operator */ + bool operator==(const aiString& other) const { + return (length == other.length && 0 == memcmp(data,other.data,length)); + } + + /** Inverse comparison operator */ + bool operator!=(const aiString& other) const { + return (length != other.length || 0 != memcmp(data,other.data,length)); + } + + /** Append a string to the string */ + void Append (const char* app) { + const size_t len = ::strlen(app); + if (!len) { + return; + } + if (length + len >= MAXLEN) { + return; + } + + memcpy(&data[length],app,len+1); + length += len; + } + + /** Clear the string - reset its length to zero */ + void Clear () { + length = 0; + data[0] = '\0'; + +#ifdef ASSIMP_BUILD_DEBUG + // Debug build: overwrite the string on its full length with ESC (27) + memset(data+1,27,MAXLEN-1); +#endif + } + + /** Returns a pointer to the underlying zero-terminated array of characters */ + const char* C_Str() const { + return data; + } + +#endif // !__cplusplus + + /** Binary length of the string excluding the terminal 0. This is NOT the + * logical length of strings containing UTF-8 multibyte sequences! It's + * the number of bytes from the beginning of the string to its end.*/ + size_t length; + + /** String buffer. Size limit is MAXLEN */ + char data[MAXLEN]; +} ; // !struct aiString + + +// ---------------------------------------------------------------------------------- +/** Standard return type for some library functions. + * Rarely used, and if, mostly in the C API. + */ +typedef enum aiReturn +{ + /** Indicates that a function was successful */ + aiReturn_SUCCESS = 0x0, + + /** Indicates that a function failed */ + aiReturn_FAILURE = -0x1, + + /** Indicates that not enough memory was available + * to perform the requested operation + */ + aiReturn_OUTOFMEMORY = -0x3, + + /** @cond never + * Force 32-bit size enum + */ + _AI_ENFORCE_ENUM_SIZE = 0x7fffffff + + /// @endcond +} aiReturn; // !enum aiReturn + +// just for backwards compatibility, don't use these constants anymore +#define AI_SUCCESS aiReturn_SUCCESS +#define AI_FAILURE aiReturn_FAILURE +#define AI_OUTOFMEMORY aiReturn_OUTOFMEMORY + +// ---------------------------------------------------------------------------------- +/** Seek origins (for the virtual file system API). + * Much cooler than using SEEK_SET, SEEK_CUR or SEEK_END. + */ +enum aiOrigin +{ + /** Beginning of the file */ + aiOrigin_SET = 0x0, + + /** Current position of the file pointer */ + aiOrigin_CUR = 0x1, + + /** End of the file, offsets must be negative */ + aiOrigin_END = 0x2, + + /** @cond never + * Force 32-bit size enum + */ + _AI_ORIGIN_ENFORCE_ENUM_SIZE = 0x7fffffff + + /// @endcond +}; // !enum aiOrigin + +// ---------------------------------------------------------------------------------- +/** @brief Enumerates predefined log streaming destinations. + * Logging to these streams can be enabled with a single call to + * #LogStream::createDefaultStream. + */ +enum aiDefaultLogStream +{ + /** Stream the log to a file */ + aiDefaultLogStream_FILE = 0x1, + + /** Stream the log to std::cout */ + aiDefaultLogStream_STDOUT = 0x2, + + /** Stream the log to std::cerr */ + aiDefaultLogStream_STDERR = 0x4, + + /** MSVC only: Stream the log the the debugger + * (this relies on OutputDebugString from the Win32 SDK) + */ + aiDefaultLogStream_DEBUGGER = 0x8, + + /** @cond never + * Force 32-bit size enum + */ + _AI_DLS_ENFORCE_ENUM_SIZE = 0x7fffffff + /// @endcond +}; // !enum aiDefaultLogStream + +// just for backwards compatibility, don't use these constants anymore +#define DLS_FILE aiDefaultLogStream_FILE +#define DLS_STDOUT aiDefaultLogStream_STDOUT +#define DLS_STDERR aiDefaultLogStream_STDERR +#define DLS_DEBUGGER aiDefaultLogStream_DEBUGGER + +// ---------------------------------------------------------------------------------- +/** Stores the memory requirements for different components (e.g. meshes, materials, + * animations) of an import. All sizes are in bytes. + * @see Importer::GetMemoryRequirements() +*/ +struct aiMemoryInfo +{ +#ifdef __cplusplus + + /** Default constructor */ + aiMemoryInfo() + : textures (0) + , materials (0) + , meshes (0) + , nodes (0) + , animations (0) + , cameras (0) + , lights (0) + , total (0) + {} + +#endif + + /** Storage allocated for texture data */ + unsigned int textures; + + /** Storage allocated for material data */ + unsigned int materials; + + /** Storage allocated for mesh data */ + unsigned int meshes; + + /** Storage allocated for node data */ + unsigned int nodes; + + /** Storage allocated for animation data */ + unsigned int animations; + + /** Storage allocated for camera data */ + unsigned int cameras; + + /** Storage allocated for light data */ + unsigned int lights; + + /** Total storage allocated for the full import. */ + unsigned int total; +}; // !struct aiMemoryInfo + +#ifdef __cplusplus +} +#endif //! __cplusplus + +// Include implementation files +#include "vector2.inl" +#include "vector3.inl" +#include "color4.inl" +#include "quaternion.inl" +#include "matrix3x3.inl" +#include "matrix4x4.inl" + +#endif // AI_TYPES_H_INC diff --git a/Assimp/include/assimp/vector2.h b/Assimp/include/assimp/vector2.h new file mode 100644 index 0000000..564d1f8 --- /dev/null +++ b/Assimp/include/assimp/vector2.h @@ -0,0 +1,116 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 vector2.h + * @brief 2D vector structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_VECTOR2D_H_INC +#define AI_VECTOR2D_H_INC + +#ifdef __cplusplus +# include +#else +# include +#endif + +#include "./Compiler/pushpack1.h" +#include "defs.h" + +// ---------------------------------------------------------------------------------- +/** Represents a two-dimensional vector. + */ + +#ifdef __cplusplus +template +class aiVector2t +{ +public: + + aiVector2t () : x(), y() {} + aiVector2t (TReal _x, TReal _y) : x(_x), y(_y) {} + explicit aiVector2t (TReal _xyz) : x(_xyz), y(_xyz) {} + aiVector2t (const aiVector2t& o) : x(o.x), y(o.y) {} + +public: + + void Set( TReal pX, TReal pY); + TReal SquareLength() const ; + TReal Length() const ; + aiVector2t& Normalize(); + +public: + + const aiVector2t& operator += (const aiVector2t& o); + const aiVector2t& operator -= (const aiVector2t& o); + const aiVector2t& operator *= (TReal f); + const aiVector2t& operator /= (TReal f); + + TReal operator[](unsigned int i) const; + TReal& operator[](unsigned int i); + + bool operator== (const aiVector2t& other) const; + bool operator!= (const aiVector2t& other) const; + + bool Equal(const aiVector2t& other, TReal epsilon = 1e-6) const; + + aiVector2t& operator= (TReal f); + const aiVector2t SymMul(const aiVector2t& o); + + template + operator aiVector2t () const; + + TReal x, y; +} PACK_STRUCT; + +typedef aiVector2t aiVector2D; + +#else + +struct aiVector2D { + ai_real x, y; +}; + +#endif // __cplusplus + +#include "./Compiler/poppack1.h" + +#endif // AI_VECTOR2D_H_INC diff --git a/Assimp/include/assimp/vector2.inl b/Assimp/include/assimp/vector2.inl new file mode 100644 index 0000000..5ce13ee --- /dev/null +++ b/Assimp/include/assimp/vector2.inl @@ -0,0 +1,227 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 vector2.inl + * @brief Inline implementation of aiVector2t operators + */ +#pragma once +#ifndef AI_VECTOR2D_INL_INC +#define AI_VECTOR2D_INL_INC + +#ifdef __cplusplus +#include "vector2.h" + +#include + +// ------------------------------------------------------------------------------------------------ +template +template +aiVector2t::operator aiVector2t () const { + return aiVector2t(static_cast(x),static_cast(y)); +} +// ------------------------------------------------------------------------------------------------ +template +void aiVector2t::Set( TReal pX, TReal pY) { + x = pX; y = pY; +} + +// ------------------------------------------------------------------------------------------------ +template +TReal aiVector2t::SquareLength() const { + return x*x + y*y; +} + +// ------------------------------------------------------------------------------------------------ +template +TReal aiVector2t::Length() const { + return std::sqrt( SquareLength()); +} + +// ------------------------------------------------------------------------------------------------ +template +aiVector2t& aiVector2t::Normalize() { + *this /= Length(); + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +const aiVector2t& aiVector2t::operator += (const aiVector2t& o) { + x += o.x; y += o.y; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +const aiVector2t& aiVector2t::operator -= (const aiVector2t& o) { + x -= o.x; y -= o.y; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +const aiVector2t& aiVector2t::operator *= (TReal f) { + x *= f; y *= f; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +const aiVector2t& aiVector2t::operator /= (TReal f) { + x /= f; y /= f; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +TReal aiVector2t::operator[](unsigned int i) const { + return *(&x + i); +} + +// ------------------------------------------------------------------------------------------------ +template +TReal& aiVector2t::operator[](unsigned int i) { + return *(&x + i); +} + +// ------------------------------------------------------------------------------------------------ +template +bool aiVector2t::operator== (const aiVector2t& other) const { + return x == other.x && y == other.y; +} + +// ------------------------------------------------------------------------------------------------ +template +bool aiVector2t::operator!= (const aiVector2t& other) const { + return x != other.x || y != other.y; +} + +// --------------------------------------------------------------------------- +template +bool aiVector2t::Equal(const aiVector2t& other, TReal epsilon) const { + return + std::abs(x - other.x) <= epsilon && + std::abs(y - other.y) <= epsilon; +} + +// ------------------------------------------------------------------------------------------------ +template +aiVector2t& aiVector2t::operator= (TReal f) { + x = y = f; + return *this; +} + +// ------------------------------------------------------------------------------------------------ +template +const aiVector2t aiVector2t::SymMul(const aiVector2t& o) { + return aiVector2t(x*o.x,y*o.y); +} + + +// ------------------------------------------------------------------------------------------------ +// symmetric addition +template +inline aiVector2t operator + (const aiVector2t& v1, const aiVector2t& v2) +{ + return aiVector2t( v1.x + v2.x, v1.y + v2.y); +} + +// ------------------------------------------------------------------------------------------------ +// symmetric subtraction +template +inline aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) +{ + return aiVector2t( v1.x - v2.x, v1.y - v2.y); +} + +// ------------------------------------------------------------------------------------------------ +// scalar product +template +inline TReal operator * (const aiVector2t& v1, const aiVector2t& v2) +{ + return v1.x*v2.x + v1.y*v2.y; +} + +// ------------------------------------------------------------------------------------------------ +// scalar multiplication +template +inline aiVector2t operator * ( TReal f, const aiVector2t& v) +{ + return aiVector2t( f*v.x, f*v.y); +} + +// ------------------------------------------------------------------------------------------------ +// and the other way around +template +inline aiVector2t operator * ( const aiVector2t& v, TReal f) +{ + return aiVector2t( f*v.x, f*v.y); +} + +// ------------------------------------------------------------------------------------------------ +// scalar division +template +inline aiVector2t operator / ( const aiVector2t& v, TReal f) +{ + + return v * (1/f); +} + +// ------------------------------------------------------------------------------------------------ +// vector division +template +inline aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) +{ + return aiVector2t(v.x / v2.x,v.y / v2.y); +} + +// ------------------------------------------------------------------------------------------------ +// vector negation +template +inline aiVector2t operator - ( const aiVector2t& v) +{ + return aiVector2t( -v.x, -v.y); +} + +#endif + +#endif // AI_VECTOR2D_INL_INC diff --git a/Assimp/include/assimp/vector3.h b/Assimp/include/assimp/vector3.h new file mode 100644 index 0000000..641dab7 --- /dev/null +++ b/Assimp/include/assimp/vector3.h @@ -0,0 +1,145 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 vector3.h + * @brief 3D vector structure, including operators when compiling in C++ + */ +#pragma once +#ifndef AI_VECTOR3D_H_INC +#define AI_VECTOR3D_H_INC + +#ifdef __cplusplus +# include +#else +# include +#endif + +#include "defs.h" + +#ifdef __cplusplus + +template class aiMatrix3x3t; +template class aiMatrix4x4t; + +// --------------------------------------------------------------------------- +/** Represents a three-dimensional vector. */ +template +class aiVector3t +{ +public: + aiVector3t() : x(), y(), z() {} + aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + aiVector3t( const aiVector3t& o ) : x(o.x), y(o.y), z(o.z) {} + +public: + + // combined operators + const aiVector3t& operator += (const aiVector3t& o); + const aiVector3t& operator -= (const aiVector3t& o); + const aiVector3t& operator *= (TReal f); + const aiVector3t& operator /= (TReal f); + + // transform vector by matrix + aiVector3t& operator *= (const aiMatrix3x3t& mat); + aiVector3t& operator *= (const aiMatrix4x4t& mat); + + // access a single element + TReal operator[](unsigned int i) const; + TReal& operator[](unsigned int i); + + // comparison + bool operator== (const aiVector3t& other) const; + bool operator!= (const aiVector3t& other) const; + bool operator < (const aiVector3t& other) const; + + bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; + + template + operator aiVector3t () const; + +public: + /** @brief Set the components of a vector + * @param pX X component + * @param pY Y component + * @param pZ Z component */ + void Set( TReal pX, TReal pY, TReal pZ); + + /** @brief Get the squared length of the vector + * @return Square length */ + TReal SquareLength() const; + + /** @brief Get the length of the vector + * @return length */ + TReal Length() const; + + + /** @brief Normalize the vector */ + aiVector3t& Normalize(); + + /** @brief Normalize the vector with extra check for zero vectors */ + aiVector3t& NormalizeSafe(); + + /** @brief Componentwise multiplication of two vectors + * + * Note that vec*vec yields the dot product. + * @param o Second factor */ + const aiVector3t SymMul(const aiVector3t& o); + + TReal x, y, z; +}; + + +typedef aiVector3t aiVector3D; + +#else + +struct aiVector3D { + ai_real x, y, z; +}; + +#endif // __cplusplus + +#ifdef __cplusplus + +#endif // __cplusplus + +#endif // AI_VECTOR3D_H_INC diff --git a/Assimp/include/assimp/vector3.inl b/Assimp/include/assimp/vector3.inl new file mode 100644 index 0000000..a074bb2 --- /dev/null +++ b/Assimp/include/assimp/vector3.inl @@ -0,0 +1,260 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 vector3.inl + * @brief Inline implementation of aiVector3t operators + */ +#pragma once +#ifndef AI_VECTOR3D_INL_INC +#define AI_VECTOR3D_INL_INC + +#ifdef __cplusplus +#include "vector3.h" + +#include + +// ------------------------------------------------------------------------------------------------ +/** Transformation of a vector by a 3x3 matrix */ +template +inline aiVector3t operator * (const aiMatrix3x3t& pMatrix, const aiVector3t& pVector) +{ + aiVector3t res; + res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z; + res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z; + res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z; + return res; +} + +// ------------------------------------------------------------------------------------------------ +/** Transformation of a vector by a 4x4 matrix */ +template +inline aiVector3t operator * (const aiMatrix4x4t& pMatrix, const aiVector3t& pVector) +{ + aiVector3t res; + res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4; + res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4; + res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z + pMatrix.c4; + return res; +} +// ------------------------------------------------------------------------------------------------ +template +template +aiVector3t::operator aiVector3t () const { + return aiVector3t(static_cast(x),static_cast(y),static_cast(z)); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE void aiVector3t::Set( TReal pX, TReal pY, TReal pZ) { + x = pX; y = pY; z = pZ; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE TReal aiVector3t::SquareLength() const { + return x*x + y*y + z*z; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE TReal aiVector3t::Length() const { + return std::sqrt( SquareLength()); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiVector3t& aiVector3t::Normalize() { + *this /= Length(); return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiVector3t& aiVector3t::NormalizeSafe() { + TReal len = Length(); + if (len > static_cast(0)) + *this /= len; + return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiVector3t& aiVector3t::operator += (const aiVector3t& o) { + x += o.x; y += o.y; z += o.z; return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiVector3t& aiVector3t::operator -= (const aiVector3t& o) { + x -= o.x; y -= o.y; z -= o.z; return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiVector3t& aiVector3t::operator *= (TReal f) { + x *= f; y *= f; z *= f; return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiVector3t& aiVector3t::operator /= (TReal f) { + x /= f; y /= f; z /= f; return *this; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiVector3t& aiVector3t::operator *= (const aiMatrix3x3t& mat){ + return(*this = mat * (*this)); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE aiVector3t& aiVector3t::operator *= (const aiMatrix4x4t& mat){ + return(*this = mat * (*this)); +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE TReal aiVector3t::operator[](unsigned int i) const { +// return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + break; + } + return x; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE TReal& aiVector3t::operator[](unsigned int i) { +// return *(&x + i); + switch (i) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + break; + } + return x; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool aiVector3t::operator== (const aiVector3t& other) const { + return x == other.x && y == other.y && z == other.z; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool aiVector3t::operator!= (const aiVector3t& other) const { + return x != other.x || y != other.y || z != other.z; +} +// --------------------------------------------------------------------------- +template +AI_FORCE_INLINE bool aiVector3t::Equal(const aiVector3t& other, TReal epsilon) const { + return + std::abs(x - other.x) <= epsilon && + std::abs(y - other.y) <= epsilon && + std::abs(z - other.z) <= epsilon; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE bool aiVector3t::operator < (const aiVector3t& other) const { + return x != other.x ? x < other.x : y != other.y ? y < other.y : z < other.z; +} +// ------------------------------------------------------------------------------------------------ +template +AI_FORCE_INLINE const aiVector3t aiVector3t::SymMul(const aiVector3t& o) { + return aiVector3t(x*o.x,y*o.y,z*o.z); +} +// ------------------------------------------------------------------------------------------------ +// symmetric addition +template +AI_FORCE_INLINE aiVector3t operator + (const aiVector3t& v1, const aiVector3t& v2) { + return aiVector3t( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); +} +// ------------------------------------------------------------------------------------------------ +// symmetric subtraction +template +AI_FORCE_INLINE aiVector3t operator - (const aiVector3t& v1, const aiVector3t& v2) { + return aiVector3t( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); +} +// ------------------------------------------------------------------------------------------------ +// scalar product +template +AI_FORCE_INLINE TReal operator * (const aiVector3t& v1, const aiVector3t& v2) { + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} +// ------------------------------------------------------------------------------------------------ +// scalar multiplication +template +AI_FORCE_INLINE aiVector3t operator * ( TReal f, const aiVector3t& v) { + return aiVector3t( f*v.x, f*v.y, f*v.z); +} +// ------------------------------------------------------------------------------------------------ +// and the other way around +template +AI_FORCE_INLINE aiVector3t operator * ( const aiVector3t& v, TReal f) { + return aiVector3t( f*v.x, f*v.y, f*v.z); +} +// ------------------------------------------------------------------------------------------------ +// scalar division +template +AI_FORCE_INLINE aiVector3t operator / ( const aiVector3t& v, TReal f) { + return v * (1/f); +} +// ------------------------------------------------------------------------------------------------ +// vector division +template +AI_FORCE_INLINE aiVector3t operator / ( const aiVector3t& v, const aiVector3t& v2) { + return aiVector3t(v.x / v2.x,v.y / v2.y,v.z / v2.z); +} +// ------------------------------------------------------------------------------------------------ +// cross product +template +AI_FORCE_INLINE aiVector3t operator ^ ( const aiVector3t& v1, const aiVector3t& v2) { + return aiVector3t( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); +} +// ------------------------------------------------------------------------------------------------ +// vector negation +template +AI_FORCE_INLINE aiVector3t operator - ( const aiVector3t& v) { + return aiVector3t( -v.x, -v.y, -v.z); +} + +// ------------------------------------------------------------------------------------------------ + +#endif // __cplusplus +#endif // AI_VECTOR3D_INL_INC diff --git a/Assimp/include/assimp/version.h b/Assimp/include/assimp/version.h new file mode 100644 index 0000000..d1821fa --- /dev/null +++ b/Assimp/include/assimp/version.h @@ -0,0 +1,108 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, 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 version.h + * @brief Functions to query the version of the Assimp runtime, check + * compile flags, ... + */ +#pragma once +#ifndef AI_VERSION_H_INC +#define AI_VERSION_H_INC + +#include "defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +/** @brief Returns a string with legal copyright and licensing information + * about Assimp. The string may include multiple lines. + * @return Pointer to static string. + */ +ASSIMP_API const char* aiGetLegalString (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the current minor version number of Assimp. + * @return Minor version of the Assimp runtime the application was + * linked/built against + */ +ASSIMP_API unsigned int aiGetVersionMinor (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the current major version number of Assimp. + * @return Major version of the Assimp runtime the application was + * linked/built against + */ +ASSIMP_API unsigned int aiGetVersionMajor (void); + +// --------------------------------------------------------------------------- +/** @brief Returns the repository revision of the Assimp runtime. + * @return SVN Repository revision number of the Assimp runtime the + * application was linked/built against. + */ +ASSIMP_API unsigned int aiGetVersionRevision (void); + +//! Assimp was compiled as a shared object (Windows: DLL) +#define ASSIMP_CFLAGS_SHARED 0x1 +//! Assimp was compiled against STLport +#define ASSIMP_CFLAGS_STLPORT 0x2 +//! Assimp was compiled as a debug build +#define ASSIMP_CFLAGS_DEBUG 0x4 + +//! Assimp was compiled with ASSIMP_BUILD_BOOST_WORKAROUND defined +#define ASSIMP_CFLAGS_NOBOOST 0x8 +//! Assimp was compiled with ASSIMP_BUILD_SINGLETHREADED defined +#define ASSIMP_CFLAGS_SINGLETHREADED 0x10 + +// --------------------------------------------------------------------------- +/** @brief Returns assimp's compile flags + * @return Any bitwise combination of the ASSIMP_CFLAGS_xxx constants. + */ +ASSIMP_API unsigned int aiGetCompileFlags (void); + +#ifdef __cplusplus +} // end extern "C" +#endif + +#endif // !! #ifndef AI_VERSION_H_INC + diff --git a/Graphics2/DirectXFramework.cpp b/Graphics2/DirectXFramework.cpp index c6a753e..3c13fe1 100644 --- a/Graphics2/DirectXFramework.cpp +++ b/Graphics2/DirectXFramework.cpp @@ -22,8 +22,8 @@ DirectXFramework::DirectXFramework(unsigned int width, unsigned int height) : Fr // Initialise vectors used to create camera. We will move these // to a separate Camera class later - _eyePosition = XMFLOAT4(0.0f, 1.0f, -15.0f, 0.0f); - _focalPointPosition = XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f); + _eyePosition = XMFLOAT4(0.0f, 20.0f, -90.0f, 0.0f); + _focalPointPosition = XMFLOAT4(0.0f, 20.0f, 0.0f, 0.0f); _upVector = XMFLOAT4(0.0f, 1.0f, 0.0f, 0.0f); } @@ -76,6 +76,7 @@ bool DirectXFramework::Initialise() // Create camera and projection matrices (we will look at how the // camera matrix is created from vectors later) XMStoreFloat4x4(&_projectionTransformation, XMMatrixPerspectiveFovLH(XM_PIDIV4, (float)GetWindowWidth() / GetWindowHeight(), 1.0f, 10000.0f)); + _resourceManager = make_shared(); _sceneGraph = make_shared(); CreateSceneGraph(); return _sceneGraph->Initialise(); diff --git a/Graphics2/DirectXFramework.h b/Graphics2/DirectXFramework.h index 86512f4..ae618c3 100644 --- a/Graphics2/DirectXFramework.h +++ b/Graphics2/DirectXFramework.h @@ -2,6 +2,7 @@ #include #include "Framework.h" #include "DirectXCore.h" +#include "ResourceManager.h" #include "SceneGraph.h" class DirectXFramework : public Framework @@ -25,6 +26,8 @@ public: inline ComPtr GetDevice() { return _device; } inline ComPtr GetDeviceContext() { return _deviceContext; } + inline shared_ptr GetResourceManager() { return _resourceManager; } + XMMATRIX GetViewTransformation(); XMMATRIX GetProjectionTransformation(); @@ -59,5 +62,7 @@ private: float _backgroundColour[4]; bool GetDeviceAndSwapChain(); + + shared_ptr _resourceManager; }; diff --git a/Graphics2/Graphics2.cpp b/Graphics2/Graphics2.cpp index 18cc4c3..a12f274 100644 --- a/Graphics2/Graphics2.cpp +++ b/Graphics2/Graphics2.cpp @@ -7,12 +7,39 @@ void Graphics2::CreateSceneGraph() SceneGraphPointer sceneGraph = GetSceneGraph(); // This is where you add nodes to the scene graph + //shared_ptr cube = make_shared(L"Body", L"Textures\\woodbox.bmp"); + //cube->SetWorldTransform(XMMatrixScaling(5.0f, 8.0f, 2.5f) * XMMatrixTranslation(0, 23.0f, 0)); + //sceneGraph->Add(cube); + + shared_ptr node = make_shared(L"Plane1", L"Models\\Plane\\Bonanza.3DS"); + node->SetWorldTransform(XMMatrixScaling(0.5f, 0.5f, 0.5f) * XMMatrixRotationAxis(XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 90.0f * XM_PI / 180.0f) * XMMatrixTranslation(-30.0f, 20.0f, 0)); + sceneGraph->Add(node); + + SetBackgroundColour(XMFLOAT4(0.29f, 0.38f, 0.72f, 1.0f)); + + _currentRotation = 0; + _currentSideRotation = 0; + _currentPropRotation = 0; } void Graphics2::UpdateSceneGraph() { SceneGraphPointer sceneGraph = GetSceneGraph(); - // This is where you make any changes to the local world transformations to nodes - // in the scene graph + sceneGraph->SetWorldTransform((SharedMethods::RotateFromPoint(30.0f, -20.0f, 0, XMMatrixRotationAxis(XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f), _currentSideRotation * XM_PI / 180.0f))) * XMMatrixRotationAxis(XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), _currentRotation * XM_PI / 180.0f)); + + sceneGraph->Find(L"airscrew")->SetWorldTransform(SharedMethods::RotateFromPoint(0.0f, 15.471f, 14.5f, XMMatrixRotationAxis(XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), _currentPropRotation * XM_PI / 180.0f))); + + if (_currentRotation == 360) + { + _currentRotation = 0; + //_currentSideRotation = 0; + _currentPropRotation = 0; + } + else + { + _currentRotation += 1; + _currentSideRotation += 0.3; + _currentPropRotation += 100; + } } diff --git a/Graphics2/Graphics2.h b/Graphics2/Graphics2.h index cb2d36f..0147339 100644 --- a/Graphics2/Graphics2.h +++ b/Graphics2/Graphics2.h @@ -1,5 +1,10 @@ #pragma once #include "DirectXFramework.h" +#include "SharedMethods.h" +#include "TexturedCubeNode.h" +#include "MeshNode.h" +#include "SplitMeshNode.h" +#include class Graphics2 : public DirectXFramework { @@ -7,5 +12,9 @@ public: void CreateSceneGraph(); void UpdateSceneGraph(); +private: + float _currentRotation = 0.0f; + float _currentSideRotation = 0.0f; + float _currentPropRotation = 0.0f; }; diff --git a/Graphics2/Graphics2.vcxproj b/Graphics2/Graphics2.vcxproj index 6ed90e9..5732e03 100644 --- a/Graphics2/Graphics2.vcxproj +++ b/Graphics2/Graphics2.vcxproj @@ -75,6 +75,7 @@ true + ..\Assimp\include;$(IncludePath) false @@ -149,10 +150,21 @@ + + + + + + + + + + + @@ -160,11 +172,41 @@ + + + + + + + + + + + + + + + + + + + + Document + + + + + Document + + + + + diff --git a/Graphics2/Graphics2.vcxproj.filters b/Graphics2/Graphics2.vcxproj.filters index fb173ac..aee26c6 100644 --- a/Graphics2/Graphics2.vcxproj.filters +++ b/Graphics2/Graphics2.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {8d44fb5e-e3a8-497e-9825-b8031cb66d67} + @@ -45,6 +48,39 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -58,6 +94,21 @@ Resource Files + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + @@ -69,5 +120,50 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Shaders + + + Shaders + + + + + \ No newline at end of file diff --git a/Graphics2/Mesh.cpp b/Graphics2/Mesh.cpp new file mode 100644 index 0000000..f82c270 --- /dev/null +++ b/Graphics2/Mesh.cpp @@ -0,0 +1,70 @@ +#include "Mesh.h" + +// Material methods + +Material::Material(wstring materialName, XMFLOAT4 diffuseColour, XMFLOAT4 specularColour, float shininess, float opacity, ComPtr texture ) +{ + _materialName = materialName; + _diffuseColour = diffuseColour; + _specularColour = specularColour; + _shininess = shininess; + _opacity = opacity; + _texture = texture; +} + +Material::~Material(void) +{ +} + +// SubMesh methods + +SubMesh::SubMesh(ComPtr vertexBuffer, + ComPtr indexBuffer, + size_t vertexCount, + size_t indexCount, + shared_ptr material) +{ + _vertexBuffer = vertexBuffer; + _indexBuffer = indexBuffer; + _vertexCount = vertexCount; + _indexCount = indexCount; + _material = material; +} + +SubMesh::~SubMesh(void) +{ +} + +// Mesh methods + +size_t Mesh::GetSubMeshCount() +{ + return _subMeshList.size(); +} + +shared_ptr Mesh::GetSubMesh(unsigned int i) +{ + if (i >= 0 && i < _subMeshList.size()) + { + return _subMeshList[i]; + } + else + { + return nullptr; + } +} + +void Mesh::AddSubMesh(shared_ptr subMesh) +{ + _subMeshList.push_back(subMesh); +} + +shared_ptr Mesh::GetRootNode() +{ + return _rootNode; +} + +void Mesh::SetRootNode(shared_ptr node) +{ + _rootNode = node; +} diff --git a/Graphics2/Mesh.h b/Graphics2/Mesh.h new file mode 100644 index 0000000..aed19c1 --- /dev/null +++ b/Graphics2/Mesh.h @@ -0,0 +1,92 @@ +#pragma once +#include "core.h" +#include "DirectXCore.h" +#include + +// Core material class. Ideally, this should be extended to include more material attributes that can be +// recovered from Assimp, but this handles the basics. + +class Material +{ +public: + Material(wstring materialName, XMFLOAT4 diffuseColour, XMFLOAT4 specularColour, float shininess, float opacity, ComPtr texture ); + ~Material(); + + inline wstring GetMaterialName() { return _materialName; } + inline XMFLOAT4 GetDiffuseColour() { return _diffuseColour; } + inline XMFLOAT4 GetSpecularColour() { return _specularColour; } + inline float GetShininess() { return _shininess; } + inline float GetOpacity() { return _opacity; } + inline ComPtr GetTexture() { return _texture; } + +private: + wstring _materialName; + XMFLOAT4 _diffuseColour; + XMFLOAT4 _specularColour; + float _shininess; + float _opacity; + ComPtr _texture; +}; + +// Basic SubMesh class. A Mesh consists of one or more sub-meshes. The submesh provides everything that is needed to +// draw the sub-mesh. + +class SubMesh +{ +public: + SubMesh(ComPtr vertexBuffer, + ComPtr indexBuffer, + size_t vertexCount, + size_t indexCount, + shared_ptr material); + ~SubMesh(); + + inline ComPtr GetVertexBuffer() { return _vertexBuffer; } + inline ComPtr GetIndexBuffer() { return _indexBuffer; } + inline shared_ptr GetMaterial() { return _material; } + inline size_t GetVertexCount() { return _vertexCount; } + inline size_t GetIndexCount() { return _indexCount; } + +private: + ComPtr _vertexBuffer; + ComPtr _indexBuffer; + shared_ptr _material; + size_t _vertexCount; + size_t _indexCount; +}; + +// The core Mesh class. A Mesh corresponds to a scene in ASSIMP. A mesh consists of one or more sub-meshes. + +class Node +{ +public: + inline void SetName(wstring name) { _name = name; } + inline wstring GetName() { return _name; } + inline size_t GetMeshCount() { return _meshIndices.size(); } + inline unsigned int GetMesh(unsigned int index) { return _meshIndices[index]; } + inline void AddMesh(unsigned int meshIndex) { _meshIndices.push_back(meshIndex); } + inline size_t GetChildrenCount() { return _children.size(); } + inline shared_ptr GetChild(unsigned int index) { return _children[index]; } + inline void AddChild(shared_ptr node) { _children.push_back(node); } + +private: + wstring _name; + vector _meshIndices; + vector> _children; +}; + +class Mesh +{ +public: + size_t GetSubMeshCount(); + shared_ptr GetSubMesh(unsigned int i); + void AddSubMesh(shared_ptr subMesh); + shared_ptr GetRootNode(); + void SetRootNode(shared_ptr node); + +private: + vector> _subMeshList; + shared_ptr _rootNode; +}; + + diff --git a/Graphics2/MeshNode.cpp b/Graphics2/MeshNode.cpp new file mode 100644 index 0000000..b3ca5c5 --- /dev/null +++ b/Graphics2/MeshNode.cpp @@ -0,0 +1,29 @@ +#include "MeshNode.h" + +bool MeshNode::Initialise() +{ + _resourceManager = DirectXFramework::GetDXFramework()->GetResourceManager(); + _renderer = dynamic_pointer_cast(_resourceManager->GetRenderer(L"PNT")); + _mesh = _resourceManager->GetMesh(_modelName); + if (_mesh == nullptr) + { + return false; + } + return _renderer->Initialise(); +} + +void MeshNode::Shutdown() +{ + _resourceManager->ReleaseMesh(_modelName); +} + +void MeshNode::Render() +{ + _renderer->SetMesh(_mesh); + _renderer->SetWorldTransformation(XMLoadFloat4x4(&_combinedWorldTransformation)); + _renderer->SetCameraPosition(XMFLOAT4(0.0f, 0.0f, -100.0f, 1.0f)); + _renderer->SetAmbientLight(XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f)); + _renderer->SetDirectionalLight(XMVectorSet(0.0f, -1.0f, 1.0f, 0.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f)); + _renderer->Render(); +} + diff --git a/Graphics2/MeshNode.h b/Graphics2/MeshNode.h new file mode 100644 index 0000000..fba6584 --- /dev/null +++ b/Graphics2/MeshNode.h @@ -0,0 +1,22 @@ +#pragma once +#include "SceneNode.h" +#include "DirectXFramework.h" +#include "MeshRenderer.h" + +class MeshNode : public SceneNode +{ +public: + MeshNode(wstring name, wstring modelName) : SceneNode(name) { _modelName = modelName; } + + bool Initialise(); + void Render(); + void Shutdown(); + +private: + shared_ptr _renderer; + + wstring _modelName; + shared_ptr _resourceManager; + shared_ptr _mesh; +}; + diff --git a/Graphics2/MeshRenderer.cpp b/Graphics2/MeshRenderer.cpp new file mode 100644 index 0000000..aa1d1a9 --- /dev/null +++ b/Graphics2/MeshRenderer.cpp @@ -0,0 +1,234 @@ +#include "MeshRenderer.h" +#include "DirectXFramework.h" + +void MeshRenderer::SetMesh(shared_ptr mesh) +{ + _mesh = mesh; +} + +void MeshRenderer::SetWorldTransformation(FXMMATRIX worldTransformation) +{ + XMStoreFloat4x4(&_worldTransformation, worldTransformation); +} + +void MeshRenderer::SetAmbientLight(XMFLOAT4 ambientLight) +{ + _ambientLight = ambientLight; +} + +void MeshRenderer::SetDirectionalLight(FXMVECTOR lightVector, XMFLOAT4 lightColour) +{ + _directionalLightColour = lightColour; + XMStoreFloat4(&_directionalLightVector, lightVector); +} + +void MeshRenderer::SetCameraPosition(XMFLOAT4 cameraPosition) +{ + _cameraPosition = cameraPosition; +} + +bool MeshRenderer::Initialise() +{ + _device = DirectXFramework::GetDXFramework()->GetDevice(); + _deviceContext = DirectXFramework::GetDXFramework()->GetDeviceContext(); + BuildShaders(); + BuildVertexLayout(); + BuildConstantBuffer(); + BuildBlendState(); + BuildRendererState(); + return true; +} + +void MeshRenderer::RenderNode(shared_ptr node, bool renderTransparent) +{ + unsigned int subMeshCount = (unsigned int)node->GetMeshCount(); + // Loop through all submeshes in the mesh, rendering them + for (unsigned int i = 0; i < subMeshCount; i++) + { + unsigned int meshIndex = node->GetMesh(i); + shared_ptr subMesh = _mesh->GetSubMesh(meshIndex); + shared_ptr material = subMesh->GetMaterial(); + float opacity = material->GetOpacity(); + if ((renderTransparent && opacity < 1.0f) || + (!renderTransparent && opacity == 1.0f)) + { + UINT stride = sizeof(VERTEX); + UINT offset = 0; + _vertexBuffer = subMesh->GetVertexBuffer(); + _deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(), &stride, &offset); + _indexBuffer = subMesh->GetIndexBuffer(); + _deviceContext->IASetIndexBuffer(_indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0); + _cBuffer.DiffuseCoefficient = material->GetDiffuseColour(); + _cBuffer.SpecularCoefficient = material->GetSpecularColour(); + _cBuffer.Shininess = material->GetShininess(); + _cBuffer.Opacity = opacity; + // Update the constant buffer + _deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf()); + _deviceContext->UpdateSubresource(_constantBuffer.Get(), 0, 0, &_cBuffer, 0, 0); + _texture = material->GetTexture(); + _deviceContext->PSSetShaderResources(0, 1, _texture.GetAddressOf()); + _deviceContext->PSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf()); + _deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + _deviceContext->DrawIndexed(static_cast(subMesh->GetIndexCount()), 0, 0); + } + } + // Render the children + unsigned int childrenCount = (unsigned int)node->GetChildrenCount(); + // Loop through all submeshes in the mesh, rendering them + for (unsigned int i = 0; i < childrenCount; i++) + { + RenderNode(node->GetChild(i), renderTransparent); + } +} + +void MeshRenderer::Render() +{ + // Turn off back face culling while we render a mesh. + // We do this since ASSIMP does not appear to be setting the + // TWOSIDED property on materials correctly. Without turning off + // back face culling, some materials do not render correctly. + _deviceContext->RSSetState(_noCullRasteriserState.Get()); + + XMMATRIX projectionTransformation = DirectXFramework::GetDXFramework()->GetProjectionTransformation(); + XMMATRIX viewTransformation = DirectXFramework::GetDXFramework()->GetViewTransformation(); + + XMMATRIX completeTransformation = XMLoadFloat4x4(&_worldTransformation) * viewTransformation * projectionTransformation; + + // Draw the first cube + + _cBuffer.CompleteTransformation = completeTransformation; + _cBuffer.WorldTransformation = XMLoadFloat4x4(&_worldTransformation); + _cBuffer.AmbientColor = _ambientLight; + _cBuffer.LightVector = XMVector4Normalize(XMLoadFloat4(&_directionalLightVector)); + _cBuffer.LightColor = _directionalLightColour; + _cBuffer.CameraPosition = _cameraPosition; + + _deviceContext->VSSetShader(_vertexShader.Get(), 0, 0); + _deviceContext->PSSetShader(_pixelShader.Get(), 0, 0); + _deviceContext->IASetInputLayout(_layout.Get()); + + // Set the blend state correctly to handle opacity + float blendFactors[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + _deviceContext->OMSetBlendState(_transparentBlendState.Get(), blendFactors, 0xffffffff); + + // We do two passes through the nodes. The first time we render nodes + // that are not transparent (i.e. their opacity == 1.0f). + RenderNode(_mesh->GetRootNode(), false); + // Now we render any transparent nodes + // We have to do this since blending always blends the submesh with + // whatever is in the render target. If we render a transparent node + // first, it will be opaque. + RenderNode(_mesh->GetRootNode(), true); + + // Turn back face culling back on in case another renderer + // relies on it + _deviceContext->RSSetState(_defaultRasteriserState.Get()); +} + +void MeshRenderer::Shutdown(void) +{ +} + +void MeshRenderer::BuildShaders() +{ + DWORD shaderCompileFlags = 0; +#if defined( _DEBUG ) + shaderCompileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + ComPtr compilationMessages = nullptr; + + //Compile vertex shader + HRESULT hr = D3DCompileFromFile(L"TexturedShaders.hlsl", + nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, + "VShader", "vs_5_0", + shaderCompileFlags, 0, + _vertexShaderByteCode.GetAddressOf(), + compilationMessages.GetAddressOf()); + + if (compilationMessages.Get() != nullptr) + { + // If there were any compilation messages, display them + MessageBoxA(0, (char*)compilationMessages->GetBufferPointer(), 0, 0); + } + // Even if there are no compiler messages, check to make sure there were no other errors. + ThrowIfFailed(hr); + ThrowIfFailed(_device->CreateVertexShader(_vertexShaderByteCode->GetBufferPointer(), _vertexShaderByteCode->GetBufferSize(), NULL, _vertexShader.GetAddressOf())); + + // Compile pixel shader + hr = D3DCompileFromFile(L"TexturedShaders.hlsl", + nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, + "PShader", "ps_5_0", + shaderCompileFlags, 0, + _pixelShaderByteCode.GetAddressOf(), + compilationMessages.GetAddressOf()); + + if (compilationMessages.Get() != nullptr) + { + // If there were any compilation messages, display them + MessageBoxA(0, (char*)compilationMessages->GetBufferPointer(), 0, 0); + } + ThrowIfFailed(hr); + ThrowIfFailed(_device->CreatePixelShader(_pixelShaderByteCode->GetBufferPointer(), _pixelShaderByteCode->GetBufferSize(), NULL, _pixelShader.GetAddressOf())); +} + + +void MeshRenderer::BuildVertexLayout() +{ + // Create the vertex input layout. This tells DirectX the format + // of each of the vertices we are sending to it. + + D3D11_INPUT_ELEMENT_DESC vertexDesc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT , D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT , D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + ThrowIfFailed(_device->CreateInputLayout(vertexDesc, ARRAYSIZE(vertexDesc), _vertexShaderByteCode->GetBufferPointer(), _vertexShaderByteCode->GetBufferSize(), _layout.GetAddressOf())); +} + +void MeshRenderer::BuildConstantBuffer() +{ + D3D11_BUFFER_DESC bufferDesc; + ZeroMemory(&bufferDesc, sizeof(bufferDesc)); + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + bufferDesc.ByteWidth = sizeof(CBUFFER); + bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + ThrowIfFailed(_device->CreateBuffer(&bufferDesc, NULL, _constantBuffer.GetAddressOf())); +} + +void MeshRenderer::BuildBlendState() +{ + D3D11_BLEND_DESC transparentDesc = { 0 }; + transparentDesc.AlphaToCoverageEnable = false; + transparentDesc.IndependentBlendEnable = false; + transparentDesc.RenderTarget[0].BlendEnable = true; + transparentDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + transparentDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + transparentDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + transparentDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO; + transparentDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + transparentDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + transparentDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + ThrowIfFailed(_device->CreateBlendState(&transparentDesc, _transparentBlendState.GetAddressOf())); +} + +void MeshRenderer::BuildRendererState() +{ + // Set default and no cull rasteriser states + D3D11_RASTERIZER_DESC rasteriserDesc; + rasteriserDesc.FillMode = D3D11_FILL_SOLID; + rasteriserDesc.CullMode = D3D11_CULL_BACK; + rasteriserDesc.FrontCounterClockwise = false; + rasteriserDesc.DepthBias = 0; + rasteriserDesc.SlopeScaledDepthBias = 0.0f; + rasteriserDesc.DepthBiasClamp = 0.0f; + rasteriserDesc.DepthClipEnable = true; + rasteriserDesc.ScissorEnable = false; + rasteriserDesc.MultisampleEnable = false; + rasteriserDesc.AntialiasedLineEnable = false; + ThrowIfFailed(_device->CreateRasterizerState(&rasteriserDesc, _defaultRasteriserState.GetAddressOf())); + rasteriserDesc.CullMode = D3D11_CULL_NONE; + ThrowIfFailed(_device->CreateRasterizerState(&rasteriserDesc, _noCullRasteriserState.GetAddressOf())); +} + diff --git a/Graphics2/MeshRenderer.h b/Graphics2/MeshRenderer.h new file mode 100644 index 0000000..05be6e2 --- /dev/null +++ b/Graphics2/MeshRenderer.h @@ -0,0 +1,58 @@ +#pragma once +#include "SharedMethods.h" +#include "Renderer.h" +#include "Mesh.h" + +class MeshRenderer : public Renderer +{ +public: + + void SetMesh(shared_ptr mesh); + void SetWorldTransformation(FXMMATRIX worldTransformation); + void SetAmbientLight(XMFLOAT4 ambientLight); + void SetDirectionalLight(FXMVECTOR lightVector, XMFLOAT4 lightColour); + void SetCameraPosition(XMFLOAT4 cameraPosition); + bool Initialise(); + void Render(); + void Shutdown(void); + +private: + shared_ptr _mesh; + XMFLOAT4X4 _worldTransformation; + XMFLOAT4 _ambientLight; + XMFLOAT4 _directionalLightVector; + XMFLOAT4 _directionalLightColour; + XMFLOAT4 _cameraPosition; + + ComPtr _device; + ComPtr _deviceContext; + + ComPtr _vertexBuffer; + ComPtr _indexBuffer; + + ComPtr _vertexShaderByteCode = nullptr; + ComPtr _pixelShaderByteCode = nullptr; + ComPtr _vertexShader; + ComPtr _pixelShader; + ComPtr _layout; + ComPtr _constantBuffer; + + ComPtr _texture;; + + ComPtr _transparentBlendState; + + ComPtr _defaultRasteriserState; + ComPtr _noCullRasteriserState; + + CBUFFER _cBuffer; + + + void BuildShaders(); + void BuildVertexLayout(); + void BuildConstantBuffer(); + void BuildBlendState(); + void BuildRendererState(); + + void RenderNode(shared_ptr node, bool renderTransparent); +}; + diff --git a/Graphics2/Models/Plane/Bonanza.3ds b/Graphics2/Models/Plane/Bonanza.3ds new file mode 100644 index 0000000000000000000000000000000000000000..36c404da7663795ad46a824dba276be0aad2b925 GIT binary patch literal 581572 zcmeFa1$Y$8w)fpVaf7(KLy!as1eoex5Zpp=hu}^iK!OH$3-0djmg&ZIqZ@ZN4jb7> zh>^T&RS)N6&b{Zp_ulV)zwg}V`Pfh9SL;7j)g@i4dupcFZrJej6Kg>y*b0JRhJSkf zpsm&d)BmyycfCPXSnZ524LhfF8`P_F-@uR@wnTLrf1SLVnk@+FlCU-g zsU5MaAgpsjE52qeXx^Yqy@c9AlXX;;AT(X46%d5NU57~chknMI^b-Jz_Oo5PcK<;? z#s63OS-9&UZ#MO>bN`f{gF1KVmD0_m)wZ^_MO*FV<@F!5+RLQX?*Er&r*PLJ-f9~B zLEXBVH1}WfWGaIV{rky8Cv#%W_D8+uVZLWjHh2xdX>){}c)xnx02Uv8&09ahg44%#3!z!Kggb&xuVEy-Bi0K z0kCORo;<(iN4;;=Q20D0Pk#M+va&rX97d1Lm+h|aVS$hRA?EH+x&HPYEWDK`+z%|2 z7msj(%I6B@tgfY0_x>*M=~1D)IAMV@sg@HYy)2N=Pw1gKU-C4mv*@w@GxZm( zQ--^^V!efzCqFW&tCv(yeZkiF)t9zdw9ymu3db<_%LAmFZ*27R{te{#N|)I>e1H7Z zrmQ~ZABX&j`I;;mtEYOV;QMV?J4jT1)=F!M%5CU4h})tdOH|+B&cnD)r201E`&3^W zEJyWGKB|w(Q+@e`V-za4+oFwu>b>^H#z6HFsr+ixsXXPQa-AR+`^7&-asGP2k5wlB ze%XNsNDc&Vogjo_O2p5TR`_bhsT0fv3$%6kDj+QcOMJD$S8Gly{uz=Kf#;!wX z9Pl$Ku^7Ggw?k4s0qfIj8+?!2A#mFkQ>2?AZIP~+BI=N? zNH?McT5p2kRC`6q&w0Q6|y}sMYKkGBE2w0B*hC~wKh`ksEyhmY0=OQTU7M3 z^sg~9!y{;;V~wS>ad5!?7w0{7##&slMecZ1PdtLR;3N17euBRcAOs3QLa-2mm(zt< zH}&{m=}(Y5<9%!YAzkqL2rV$_2zBsOi55cq?V!Y{ONLG@VxaVBJNPVFC^s}AOOAmr=C+``oNO4|CI-Tn+rs3LD-9c3n&!{2wua6X zM=RN>G4SMfD@ZxtO1VEY26kSxf*W2jO0B^$&^_M@p4r(c^ZUoZrsmc#V)so$m0mHh zqNg=>>y%zHDyxgFu2{r4$en*RlK`}fqzvyXy-Fg`K?zN zJbGpe(L)v};RC|pYFAqboU%q)oe~Ba=W? zhN_*y;LrgZXr8{!;L*i2|N045aC|{;C3#F3v^-)3EyO9xtg&G*INb{TUsqQijtGOO z9ad2Kp084JSQzxRv@A^2-6$fXqi)gb_8yUTS zJfpip?+1=wYB-7IpT=Ctr23M}U8zQ_Iew|0biX+Qqk8S<2nsP{Q+Zzgc1CwzU!Z$a ze!QO#I`ZRFJF}9n>Z#o?ZeP_?dk2@ls;Bm*AgR6dKDGBXzEAC4QuUgi+IukVnx5KA zq;@Vwo!Y$*ZE7#+)XqUz54HO%)(cT5p=NV;%&Vv0zYW$l7x z#%YcUU9h}y+=+C-^G5n$ibxkc>c20hh;+g8MfzikNQytcYGXp9s-;U}iZnN5Nn{kJ zh^3I!&(fG8mO++Amc!Jdq#SJ+1J~x;!|wN? zic%{Ea+=x0)9Evmf~GOh;<+uzhn{Cj6=Go71-uCnHYgwPhGdGR9jGpc6kEI@x$kca z2Za-gb^935m#~42S57Eb+nVaPf7rm&vsE(RN8^pkV@r4#I9D;l>U^{Efump<^y_Ni$G2ru?CA{eJry;X)3>fs*(7V?WWdyb%t)&&D&7Eer zTs{V7cw58MDdvXVSbr5A9&19g%)Mn}Aab)U9DgBIJvJ&HHfP#E&xDoLdJd0=?hkF? zc-w@k0Ttrl)j3<(J*QRX;7V~YdXODBcKw!lYg{~3%fS9Nt7e!{Ee;-9*@JD!GDC%_ z@vt<)9%@#|H2hgT4%W7|hYCY1lqz%Lp;MYY-1zCHe5o4;*E-sRl`1Qh7RAG?G4`;& zb`#}eLL6N3!m?}9lo2c9VQQQ`d~uzqlxrCW{btz#tGPhwwKg82cG|(LsVkJAc5%?= zk}ZsFwMm)0As#&6+rstbyA;+j4z@qHf#EgxC=)lvLvJ%%I6w7>qVE(3#XsWsxa?Kx zWB$tyHgLG*QN;}N&(>K(shwMt>^1SwFxVP~f7zq#Y8wYtDqBJDo5jkL}UFjU5I-nQEkN=~_A_*^v(>ib%ObzKib zJ)AF_YF03^PiupS^U~po6{J7Db+i=Doo}HPTw6Og^F{eMus>%FJ(K5FE0Y!v7hhR} zaQ;S>SLNd1rL8SId^J#CyICC6)7yddL@)i)R&g*j-X7A&Z$4HH$K;Wv12ld%!7#Z; z96+)I^p3DowhoMg25Al;)oZ1kNQ;AV{tn<3K391&HV&Tlw1+l7b}LP%#DQxcJIEMu zUg4 z`LF`Vq_U{bX&MKuFIhw1$vyQu6XW12>ddqt>T-45!+3Hl{D6=7^$T-O8R zGOG%)aNEuSq9@NaOtOxJDnlKh&2T4Wi&HGbk9L5vD>^90y<;IN!~y0sp0B(Pj)lAZ z@Z1jEuOya;1&4ulV7uXx(y??btgdbg?`GXqww8$nS2G*9zx1BsjQXx?RXwY+s4AHg?5nX*vxRj8T(ku9=bZ8H7u!;a ziim~s%pTJ0rziukFSqc6nA_OZN{4c>aPpHaw3@$H8Br1YYi$b&V>6ZEv9U1B*#_js z21Q2wN4PcU-tAXD#Kgjyc2SK%g-l9Hs-3D72Vw0d|9WMhLD%-%#hy-=$+A^>m{m<75n-u@u6-@MD=ub9p z+oViNsQ_EbSi>I|)+pOLm4nAWY@t-sHA;=;<)GL$Te!VSuU_w028uHqh*k7zlg4HK zsou7)4ZMFTsof$aNUih$kE?I1pUuhL;<1gvgh z109_ADAhAU;Z}e>Y+kloxqmqnRz9_bCw&vuv0^D$H5lh>dZMa(9`#T4Z@utbT3XJ~ zz%~+^-m!yI(KijtTq5DlpLTF4e!21~DiXZ=*h5gC1SKUf63)!P50xzq%9fZ&Xn)-r zK3r6kL1j(T1zoJb-FdYV5*`V|8(Be2_vePI4v|plnkDqU-dd^W6$vwUT7zdCGq_nt z!aF?H)ANGz<%SnbA7cw$c1|*^yWj==b@q_8e3Mf1y%+3Bw};g}J(P=2yrAt`Tj;dw zydwPN1>evgmI}G3T=;03UXX?#W_xc_hQIUzr><78sEf4{e%TAIytITjV+Sg0?s`G! zX=|81YKEcjIWH*M=Avca4AH@uUN;TJmiof_&N|5H<)OUZEKhNMass5elV;o9NP19USbpOL@1)7ancY!HV{~6`{9}Njm754s@rMC=uQ-UmgAZ@8FG}y33$#wCCi#R4} z&x$EYKfK}n9$VN`ysgrAi8q8y#_MTqgTXJ~2Of;Hg{}uSD=*smf!y2{TISd&Pn-C| zL_7y7O*vw)j`xEKoo!*o!a2&SPX6GMXba|r&6EX-zlmP7%|+W&^tdwg#9wRtCM;}y zgH0MksrB_SW=qt=x$i7|JrT_LNT(YgV zRi9I=qBP;|0ex^{bt$a*DZQg*P37n0>-y9~Llv^eHW|u(w=?MH{2XmqIq!=;q5e(7 zo78vuE+^+#OWN{XpWc37=F%Zq`twZ&>Tg&6tRLChU3qHvL@#W)tRJ=Wl|Es;3p-$Q zUq3j$2HUXyl72>{fnD}Wk{-R?#9|kAl~z5R#Rj`?XCaq2vjJVVFl&eFtgUk=X=vO@ zmN8`$TWkA*mEYJw8t?spr4QN2#0gn!%BS|y>-k^VfMOfj@LuLpv%L1w-R~As6a7Y( zzR5v)b-sf%E!J5IDA>q;E8!u%tJ_I>y3-#wGd8o-kRa((Qj!FNqNLeFw=)0hQBtoz zyGkoYRhDcU>|mQmRg&`d^^ndTtSQY>cC%R%mtW9*X@sBiuP?J}+g(uK&g znWP_#yjE@4f@AD>v*-E|hx$tepO@$ZpB-WI?v!U0clVW2h6-%$_k--dLw~j)u(vd3 zMFqC4(LT20)Eu_FMGq;kLNB&`>@HTvd=C4$`vmKK6UT0q!V;}dvA+E>S@fC*Y^m=t zcH-h!HgM@tw)2&RH176M_TZ}g|vsn>V|yY;oC^w#`13wd8j`ds@In-)-0 z>g0Wnz3-hUZFRfE+HUG7jo5L8-BS8WUz%QLZ}+50Z&uu3xdU%h3u<|TJw16LlWf;d z%k){3ud&mvIu@65nf2To%d#^ruuGHru$HN3m|A}-n;+bgMf_OD%t8~{h7}4ccC8UR zW`B=8)HP%q^j}$t{$WCH8xQ${K=zZqNHrm8gV zT5I-XSRLtJY$tXlp}BP1s|P!AqqDSnL_fAi>@Rh1Jcw1wN|WN16lVDCXth}_QdnTz ziA=H^_L`-44@zdScAxd{R`+2q%9mu}hOVsg@9o*hIqlh>scYD-Yf)^*!b$9NObk1< zCX-#Vk7V61-(jgw!q}aMU)Vk8Fm~phrQ|*|jE(E=DzSoaHt|)EH0)dn)~RDzNzE$F zTJ5hU?U+)YH8@dMdKOlN<@9PHt@H#oc|(#E(62U28rxsG7}bCk_zsu4W;9}Z+U&2E zUZoL}J7s2)-OhHJJ}9a_`{DOipK`Y*YrZ{<{cw`l;YF=jy$6-pNTrcP>vT=8HWID3 zg+seZv@RnY2THU~UAqmJXk8{bR*~e1p_19{I#Sq;Q0a)@YIf#iYw6pjb_&@gTpF;G z&D%&>a~rZ2%Nk0C+bdX%e?#eX;#_5jbxFy3L1X1Xjgr#J7L_Ej74HTT*&P}-kPJ)y zV4GtqOP9AlWxJQpR)|wBHd06*I||sJcCFa9SIbyR)h6s%46v1FeOPGpQZ{>@8#_30 znzAV85p!(QRi7ry0Ziqs>R!3CCn?0ndu4e4eh#t~~Rfno1Hti*ywI9On z*XSZGZ>utkPABxAM^9wKzdzFloe7j$1)tVmZ!1UxyB*L+*U(EfpS{$d+|gWW?`X-g z>y%K4o*&m6hE8>5^Uc=kH%)b6!p+)8D`j}GHxS8AB$~6|c65~Jxca0%66I_6XNEr3 z)>hzw?Sv$u- zTrE~HG^%-;Sud=rcYcj)y7KzUc?0&d0nr)yvtQq_t$*4{w>BtjMtf&z<@=ZH{_Zzy z%4~&=4i==Nk9IQOvkzFgEiYNITFz3~BwOh>^$lzJ$WLl}=K;Ih*jDOv#7;`F_m_e# z1j*)nm?Y19qmccoV~Rw_-5&l%q592_6)IHUe|JMN!%YxqOIiB{i+&GD8 zF`^}sw!P>b`VLF`u14F4q<745JCVLw`n4fR-(1ngq$A(j(l#6&(58(m?VBM-zG-HM zue32qzp8ZP`E9taedj|Pcy4?{&kIQ#c(gG|q>V}1i1S6#hMgZOv?1q@Bs~BX+9(V} zk{*N#ZL9?&Ne@AVHr7Isq|-JfZKTmQCFv2U&_-G$-|V7|wFthESCVgh(Z*g0-1wr6 zJ=z4LjlC$`1fz{T+7P3SJ=zq*jX1u=XN6m1v_WQt8)Pw@+BTULZj_bB?J_IeE-Q~| zE8H}z#2Lpo(5!F+Eeq*0rig*a8pwD|5rdI6ku{O=$WT;jA!{K?4@adovNn=* z+UTu=tb-)IBr0{0b&;f(LZu$E9+Gt05Ur1_k0d=Bl?KQLNYcxp(h%7YNqQ`fS|em5 zWMgDyRLD-i6tN020oepo#A?VU$V5yL88Q*s6jMYGWK*PsDWZ(2X2|B4A_B5GvIVAy z__IHuC9);51+oSzWVgZ;F&^0(*&5jjSre5u$TmpQYoXE>*%nE9ZB)o^hbdwmWP43fUboMXZnPi0p(ZVgqDnWM^b2WJ6TQPQnzi5sq3HWEW&tWCAK= zcf%C139>t~JF*)x5tSau9!S!gqC$30Oc5m{jZ-sZPh@jU^+NVS_C~fqr4O1p-j9FvLF3k+d+`3Kn&bNeQL#i0K#Q2nM~!r{Xxx&ypQo|2#=pU+ zjOMyEX9_A~xIUV52##7h+9_xehvKM>MGirWI1H6>Tp!DsilbHx?KJ*X2aQ`Qw}<1a zBkChi(MVrm(YTE;lD^8KaT{qQ{X&4oZIqF&eB8zh6NHJVPZB0`PDD;Ydm_G0MNUOd zLrxKZXbA-7<24{va4>=F5`JD5G1;_27I7{XmI&j~UV_IZ zj>jWu$C<(H8CWanv#}X(Z_fP&tP@ha`PJD(8{sk)$6I4jSt{ z#CaH%Q>d%h3*rSNwL`%aF%x@o5qSwy#AC=y$m5tgfixhg9Ve00pW^8EE1<`ifoJJ- z^Z^U-EMA0be>aZ!R^%}(eGy4tygDJB@C!HTxS^g!VCHjML=ohGd`l4FE`YK@^0{*#USpon2unc|8 zOTUEDq3*@)s`!duc43QcQ8&lGH1xoh=##9uC##C@MqvIisF>q_RnaPeb~-AWT>|gW zba?D^qaB5JYC3GsShVT*rLm1V^zy^_`wcNg<;vl8lUNp0l&?4*wK3+aii%c_+Cu%P z$~h5N>P&3cRP-*>k(1CL%*33N@O1{3n~td&XwSygyaeB$iymhlayD9v@%?$2T8!_{ z!`B69&*gqYz&V*&> z5pG9As9Di3oMZm>nc`pX3+DznmvsM=fcG+q{Jy0*-i5TpJC4?PSJ4*l9NOdEK}Wm; z=!~;ZSNd*v)$V~;X1Xf(!7FV)yqXTcYh^NC6$j%rZwOx9hT#<~4X;`w@X9m_&px^i z(zQ;z647;tt~_)$XxEzA#%s(x^z7Q|qicxf*H>_FuKDnF=-V`Jy%{~@RzBZ5(C6*q zYgO}e2XOYtziD1A6IZ+DzfN#}r1`3IIMWw!zAoWj68WA40Z}BjgHsLcZ`*C=d$qBc&+nL^IJ`v=A*tE74lC5p6{~(Oz^Ai;2ZW zN6|@i7F|SF(M@z0Jw#8@OY|0fL|@TQ^cMrfKru)R7DL2PF-!~>Bg9CtgjiCH5=)7t z#WG@9F?QUV`-pwTeqw)dfH+W076*xg#T0RfI8+=ariy9eaB+kF*!)|LOLxe*LTRUwwT4RmbgDyXdO-U-`2z<|oqM zKaT$|{l8M@f0UQ%r?&pe^N+{-W&i5WKic@4UA(9G=YRC%%CEX9{j2;h{;KyEi|W79 zMft0)U&s7a?jQA{cF{KcDo5JC*Pf!s`p=~Q@%aB)yJ$X2|7sVJY-(Gek@SQ${ZXHa zmiyJmU!=0sW-3ehe(7WvO$Qcv|5v_$B-KS_D6ee%OGlE<4}t|`Oj6yy+DiFJBmO#O zAg1Y0Hvau;AJr3R)Y6euC%s2ye;xZ*J-_Og@x65XBU_M1U<CfYzsn76-50vLuT832KUh0d*+z>`It2>;N z()6!Mr5W7*F}XQ^z~bj|7f$omz`1Bu0O#4YPMrCtZ8%3Zd%(-h_qxluuKyCw4L<$< z+Z*8aI6I!$CT#=!?~icMjVt|fl(xWWgIjw{!2;8k#y@K$ZG{uH#}~-bHo|{B(m@|! z9PoYm_#`0h`N=?aSmQ?+qAh=)PHgQsc#pt!2TmJI(?$^e+2bEQ%0X?Qt#{f0B8?ag z@tFVbzxa&#Tc7^(H{gQ2y(d0*m^P>zo_cFA=?LF^1)-O2(b)pIRpV5}F)s?tD~TZZ zO9t<5QJ_>8;c(|6%Fb(1aC>Kgyts5V!{r%Ku<@D>)_>|`D6^s@+t;S6J*>`%VXyVrQ}X{*e&mXbuzH1~3nw2uNCD4tsx8Waj6> zpnj?dE>+($d2Kj+s!%8=2YzB<+rz=j!3@fOxW!gPg~O{d=J2faVfL+U80_C?4&}dP zFrjuR*lZPH;4oLIW>hF_Us@EQh9~>Oyru$Vx00pcM*g5XYzCWSOG_1o`h)jK9kgpG zOU`Y5VA47PJ_gm4>`VB-Vx2j}Pl%CjP4X?G+z*-@+U+hCMO#`5XpiJDEc>mZ~%<8*VaRVZ+VfQ)Qjf zad0SHA7=*B-i=TOYzl>~t<3Rvq7ADRB!-#H-7U=jwDji8?SF;9GZ!5M#5gLR?L#2k zR|i90j#2JU4S|5#0@&^JPzK!!2G&@BW)H_IlL~|3KIZovS<&GBF&J*u5+HO-lA&I| zV7S=T9Cqhq>F0F}HJR&C6U-p;;e7q{h!EH^+#GbCZP*U~5R*AQx)9s^%$r4f2g6+- z9X#zGp#Mz@hP6>T$RGbmUk>LewvhmH2A-`Jdm#wC+Y9ib?_qtL_kpkh&nrln$8IhT zGMVd=d(2>?OIvp1N+9^2Fo!-9cClHv15D<$=kI24n4gJd@22@f@6YCNY0Yug>8YQ|JUZig$-9%m z9%uN$r*I2cv*rxDf%AB#v^jYFwvg@J?gI-a>)__V?o40T52OPE`2Nv=t+n@qcKdbU z__;6hp6&w+o(holFp-sR?*k53^W`spxhr8mgJI#5d^xn~c;$785IDCiN1igOnBsH> z=W9ofoZ&G+3Can9!Ylc5*D(o(kXgYHu_8xKC}CmfP$LBH*Zm<69aw0n>ktNA+WnBb z_)SpCHVA`#WwPXV&UXz@4ur$r>RED!R^t_0T?Djh_(M(?>KuDCAQUcE$dcuC4*K-0 zFvxwJFVE>>uD|yr2xb@L%guDx^wRf07`8h{{=6YougA6a*G)_>FUFqt34$fve#qB^ zJNo32A>h$8ORlmqlufu63N7dUkmvidXR(~l(} zxns6`tXR4d-n1lqT$ClZTd`0-eolnRm@jIYEpP99Uw`|rNJw9>;p*%HzUHO0UlOL7Su5 zayx4esXNZ&j_tX!|KKvx^$l4Q@H8F=OZKEXp3QtH4F@t)iBBd+s+@O3T5ef=RNiGg<@NKm@ zv{>vT&A;yk4_aElf@u~S&b4lc0X+8m%Tm7fEn{F z!Lw{X{fQdIAs?S%_#Aq++K3m95HJYGbIvY<_ZvG{xLya}Ut$?pr|0fn$bf z!2znzF^9mA*-FjTcF^{e4vyN-RX)4f!N)8M*cLWZxo@zCzDF&gRM{JbcVmjdw2zjM z@$$2wgyaYfZd&5+wx=t{Y8Hcr7WjMmhf|fTVvZ2p$pT7$G_cvLyiDe0BVMDY4w%D| z&U-<5{5^W{mRp%_w};6b?ZEcfG)iYS?w*kQhYspkB(Z5%ykMP!2+FcLEP9t0Kru5I z*S8iMQqvPk<2~WS!)2Jhq9+u)WC4plOlJw%ZYFaVZ7_#$zea4$Qg>L1=R^3U+H6(2 ztH~TqeQ6HSyKPx$TQ~TAuuN<^LDbc$sBn`Sit(v z;f8DLT%c*H8C=UgRBddYD~!!BgZy!03<{PlSSEtJ$hKNpS2sAhUxdd;<{Q3@c7*C75|kRj!|Q0*?d>xaB)ldE(&$X`9Tz|6qu+ zwa^7NpU3;grX>}tNLP4p&aeR!H1JPyj2 zXWRwW^iMaKJt$u;bthkcd%G(Hmdlmfe;Uuqy17B@>>N35*)En8>I&88=F0o0R$`-9 zxPXO_D?fG_!yfl=hR=(z&M#xD&0OXR4Sy??gKNw*T*`NWrAP8*$Kj>*`kgMY<9ohr zZPv;#{<|}5dYLOv*t$*swVyMb?V2mk>1W5f<~u@E={(u<>D|ozvz(y*(Ofyb>xZhj zABuxMt5E(tw4`!y6-HnP`zbf;8mg?B>892UxR6 z0PCRR`h!DE{pv%bR<2@H0CCkxCWGe1II;Nt-I&+A~u)spH3e4g%pz6JDr6{MC7C?0g>zLsb*`6!?^R7klKlMDR7O-(le|671CopWW1k1f0)Y6Nc;rt(#@X=6T9edmbvLnsm z(C!)P&YCWe&{_wly3AJVzjKBlycR49o}^08U0}r+OUUk=rph~9p^9P&SsRnoteI|* z|K1F`A78AB*6v`7pM%;SU#4asbAzqzEnr5cx$6EA?$DvWB|JSeNnLK`0iE!1rsZkBr6VYFb@Z4w*Td%*)AE7O?jF8Rb)b7s$o_mb+o0hOBWmnIjj> zQ*Cy>vi7nI1gd7x|JVuTfr~3lej-BW_9v8CRb3$h_2Hpkl}F=T;X3NopL|vhu5*R^ zgDs)q@E~>ed>50s>(j^rGD6+eQ;S?7w>{o71jVUCZn>JwQEB|#Jpf9oejD7N#33{2 z`?Q!E5#t6O@p>_OX`p(-#~lXV#l4ry_UgsjZZPA52-i0GsT7sI9xn z+$lj8kkzn?I^r|>d=E=#(7ug2-_O%zj?xRw;ayHmbxVpTh;z(fYepG$kK_Thlkqe1 zvdXHs)Ds%+6XEQ`l4?*@4|u#lgp~RfRQE}q5Qpb&{`(~Lgp0SyyxhKJ4z}MLr~^xR z!+|{(@TyNYwQ)sX2-#o`o}U`2%bk6pPkr21nZ(qC5#G=vS%jlE;?xf2-mu=^49vo1 z_1;S#aH@sZ$0k+P-}m8XRc8V2_?%JR>~jHK>!0!`kKdFvE1cms)SpktQr-o-!rGQU z<(#F_OrV> z6we>GOSy8-b{*6~RXo5M@cQMHs-D~64neF??inkqr|@~6$fo&nv}J-CJlYH9zbKF^ z-3Im5L?1Z$SDq{jiE5*+K9JfsSN440U0ofG_jvfZ*s1ndwZj`v7*LoaoA>FZmci$3 z+O)>cK6l5d>oD!yCSU$--&@5t)4^ms3%3=>$?ktCHwN27mnQjgUSB`8-HKu+V;i%k zK%Vs@RDCqi0oHyJU}M5}<<&Mj6W=@WtpK05mr&OpLmykj-`bSUm*;n^uD<_z z4=a!x9cZW?_=5iPcL7pznyF1%oBGu;pYr6;JxQwl0Vk8W@G4&*|28LC?efwQ+U^t} zv(gClcz;vB%J4jK?L0~ixaeXs{_3rta@4lT>R%PmXO0x$`jnaKv6s#!e)UATJh}g& zDe8z&cau4~p7c`=e4C+mzw8EUBmu@QUZNJ;>}KLu(+}s$-~XJYj@ajEGB0%=<;(rw zEKz^M_4uPqu6+C295uDO5BS!}m#YZDbs{?@a3p?rSuI`!iL z9}^$D#>@=bHe98~MFv3KG9t_xzE*u=9RLl-nnTE-MXFP908H71`?|Lms$s6dF!-(j zUYD1sAIyT`YYgt4&Y7(qc@YE`(k)=P^9+^c_(R463;4Wiq`G*wKMb#I0m;QCs(VKV z!5_;k!2j$()f4x}`n1RA%HB>@`=1VlNfQLn&zhn7oeG5uFU+9xxbbTHzM-(`3tlT1 zq^R~6L%^;h*4d+jy7JFp@Wg0cX9o;YABIFg?}`H0e;BGJM@K-j zgZsvdTd7Hx!eBk_ca`i|OAX!<3hge6kny^nTG_7z6n|JK=Z|cpzN%OPJlmQG zav3)-wcQ^jz@w%)Soe2QJMWH!^b6*YpJJ_AVmqI865-RQN6Nh^Q4na3(Yn|(C3a5~ zbkEj7X6RkTUoFY^)sGtY)oGukjdAat_R=34_tGo-n?ska*OW>rCE)8O+`nyfUTJk6 z_r!3|yvnaV^SOWDCm)+?0i`lJs~OEq=cPJ+KD}|gzWQgD9}ENwSb4Uk8W107GDlUb zn}hT28fyHO0I>S3gM>bMwbxodF!K^2_(Wy(T4z5{PwT*cVyrqkDge@OUH7h2PCfO& z9~L@Tz#nt#sMqs?Oy=&88}1=ER#JC_27$$6b67pHf;w(+h{+sv_+SQ}6~on%U4x;_ z3LV(5i&Ev$fv_)42S>j7s`nQK!I29BoVJNnR|W)v};ch(_3uS!9#*! zjI4u|&)+H)T|;2=G~Dk!ZLMC<4}urTxR-A6Mk$sN3^xbky~^t2inmR;$-KOqX9m@D z$CQp!LrwO{X+M05aX)-A?k9i9KBUx34uJyPL*8k7Sjl-4%=f(q8~43wuRGDW*WGGz zfjrSiQioRe0|7s?WM6NjuB_t+7orQ~W)&-|(=YkM*Qh+X@`+mNk6eGSnwcZlo7+uI zPWFSLce3S(@#(6^Q9pR;pCcbV(^f6$6aen6vgOgfL)69J0$}5_pE5Kptv2)zgxyPj z${*MJtFI~r!TTGz^2nenYR9305H~JYmTHwyA07yT=tJ3Z!JG!_ZqFd-`S^$2NJ&!t zvVx#E)|ok?vf6o0Ff8r&L!Ne^p1Njl2yANiQ_gK|ua0pGhI)}dWy|;vO7;E0P_}BW zoIB1{9fD~eyapw-$X25J;r_f?HpbNsRR2^%;GXplIqOns)yF>!!ZYz$H_X(Mt3sja zzAU-TC3|&Zsc>k6d%rDOA5&U5g+Rhp+=~u9p*TMX;d|5}#yx7r>gH>1)Gj@a<2T%cA{Eb34vg|zAQO0O&!tI)UVzy_#wM4Zl}s`LQUo(^l6T~ z{7G+h$&WA?aV}rZ4MekueCgZ=OeU5ziYEAX~)d(=FmM@=Q z)IhCih&1u5<->o-q5a%c>2ie093A|aEjJ7IRf|<90nN|k$x|8ysx7LT`qd$Kv*hKQ z?<($d@!9Dj^HO(lwro@Tv2x9^B;UurZQRGE{pw8Pes!^4d2*|SXUeNcQ-4eQ(ldq%Kv?T$Yr1y!W$9oso zpIB0x=OdCW2@0F8vl&o`G}N{Nco7APt((x$DNvtj!UF`L^>{!j;rbEY@0_HM*SpG zKB6`b=}a4^zxR!Fzne*n`bVVp(4RIQT6#mYw|?-=wY)AO*_4O4bLJ_o6U%R2p!ZYP za?<-`lRjp#uRd({GoFt~Ha3g@T!2XV)((4SEGI|_AMWVyhpuHrs)smy$w@Z6v^~4z zxR#T2vPnNP@~(c}ou}L;TA#Sf>!d>H8P1$!(|9MpisA1QwRw<@<0$ido4&?y;*V4$8V8Hp&Jv9Sk;ZB8pCQ~P z(zs^o%W+bE%9mZY94Fb-Zz@OqCepZ2IkJf~j+9>;SDEWnpEgdiME$1r6SaP&Gp)br z+@^7(_G;ru{YYn8|7d(j| z2O9NsiPkOU)7G!dN%_d8e8zPx(YmGMYU@|#q~nrJ$2G2NiPk6eQ(LDpr#24gYxp>& zGqP#DQ@^zJPyJ12+Bop_EcqMeaz8+%bB{=V;A`D=+;0#`Cz4+0`CRS~h*THp* z>1<@#{fzt)k;)OtFA>QvX?i;I`+S=1L4Qc3d_?kdMDlZ*p3XKE{Lb?cDIbyY5h`d_?XM9N3h{GlKjuN%g5LXb!=*84BsKO*%Pi%7=n z10PGbTJ!iiC(`>wTK^8cH*mi|B%MgQu;aDe+EB9$YOA2PU~ z<$jAuI+664fP>sGkxl-odh|hFua-}cdIj9%<%m>{NaZ%w_=6vZNIH@90TwrTJ!DgT z?m2wiB`u#Ih2Qwh`$eR3M9tp`lJUAAk)1aCGw&CX`bmFAzt7Xue_H=U@(0v^@()C^ zX&h<&YvU?&o&19~PO?P(BtJy`B+@vLA0nGb<3|2R8%LSzBU;zmdP2X6HtDo3Y29k;G@a|Tu8r$XqIE@nLt9@mC;1Js$!{3fokZ&@3jL6_ zzR+)=O@4?-en`{PC0bXMPg`Huf3ztdk@9JJxfJEM8yBautfK7M9N1bKSU%yr0KX`3rp~P zM9N2`d_>Bp>FMn3kQjbkBIP5}afx(XO;2a<&s669B2qr0=C5Rz9Vzp>Of^_ zG4_v0{Uu%VSClWE*{-wU>y-G*ruAxhz*(aE7$WIJ(x+Uo;p>)&2a!l8f2HL^pZU{U zqWdNyl_QcL>0lQk(ft{bbRy}Ci~C6ASI8y?dHV2rwS0oKzj#Srj!5N*RPOhn7=9cg z=|s{G=}Pi?$fo+H<@0fuw0wf((yKD>7m>;lHGd@={guq^%-WTCzlhXN8aK^f@$r?Y z|Fph|w7#kTwEl@?(|FSQ*2Y!lI<0?goMegmNq&aQ=Q^!x3ccM`2D%BQU_nUnI7P5F%LPNH>1$JN%C%t^;3n~rN-cM`21 z>X){TWKPXr;XNL|zmmDXqV-7q($;4>A2-ck(YU2ca~{{>>y(J+l|a<|75aYeue5vG z-1SY2{;H1AUr~PYS51umit>@aYGd?QZFqfTYyPT@(O;2H{whhL`zs9ZW%O61lfOzf`m1D~k8I6fB^&(}>Ey3cjs7Z?=ObJ5SLsH7MPo$& z(vAKqmG{r;0Oi;2NsXU3xox|yuF+rB{hO`%t3;!}O60obfkdOfs%!LDl%M=nqS0Sb zKJr&>jQ&a+C$cqv)y3$qy72ls*mW`btF}ge)y3$q+VXnneX4JN@!m#%)ti?iTk}`R z(gXC1zXxgKLe%_KZ==6THu@_*?zm3*xZ?UtmHg2U6KOn%n!idn`m1!Kze+XwD?ZMY z*XXbK_@bYxYxGxjjs7ap=&uru{;ICgU(q;`ze?ompGbC)XIrDc>SFX)ZH@k_i_u@T zG5RYSNAg!)xc||{h5S`7qrXZv`m5eXf0b zMt_xV^jE1ye?|S*)|&D2BDdH7RmbSBXdRKis>9bSttZW2HQ_qhw61Ag(z?~wDfz1= zd|eZ1Y-yco{z~gF*_yvFAG=jsA+x5As(ye%xOr8~qjO>FA$Qjs8j-53)6Xg`ZKi&x1yPm1^`? z{QTl&HGf6tO}g~ojY||n|3@72Uyo%_R112{fgX~e#~*ZPX^&@Ea@`CydMrc4$0|q{ z`2!a8kOW04(&8A=qZEICEJF)?ZjKLMkWNvJ=rIi&e9(p-wXi{}7?Pfpr+T#z+;j{l z%t_S#j~v$c{1TP3MxEkRIABx?D(8R}#i^huN5wfQBskUPjyZ|0n4-8x9=x0rS`@h4 z86VrAa?WUZBI#**D(8mrdc2UX_*^}eb4AMs>5g%GsGPeo?vV#RH&5j}Q1?T63;w*E zw-A6S`agCA@^W5i1)}YP&(r&I(*I9`;sOQpa=vH93^{W93^{v&b~D2Wib9v3}c;!&rkK17bx-)#Uax3dds1uN4uK9 zxL!?=Fhy~iWUg05OGdi_#s{K$E1(KUJ&znjlzE(`IE)We1@$<5w+0e0K9I)hn4-8k zHF>=fj~GO8i)!x2%JJguhTNj^}Z;W;wd}f~Nt%Ft*WCA`xPkI8*TOzVC zK2J}2WBz14#VKmWk5doTW~ev7_&-!{1GJhWo8Xi8q&LBGEs)LdIegNa@h9>rt`j|# zuf@e`h18zV*Wv@ULAw$@txtL-9L09XR`~2b>8$Pv z2A}XJy$zO2LUzFC{Yme@pZupdQeAnyRd80iqFw`I0cecJ6vZ9tjwOj*F=d6X6zPZ9 z4O0|{s3)(tJ*qv?PQn-hT{tP40L2NSh(E+0n4-8q6zPXZkp?J^R9}9ac$}5KXgB1M zeu#}QMRAAv^W${Gcl)E=mq+>`_QMp#9U92%?SbzOM7uY}ETDROqm_*8j}Z(=r+5Z~ zkOMKE0qFyIWCMy5mBNqH2xlb)^?n%Zfa>kXqaIM4r=eVLj`KDY^%RVQFocsLB2avx zR9^1@R8!F&gfS8Zb5gVfiVsAQe~7~{Me&qIa6OerQlL0Y6itCR8dDVaM~g2+@qfmk zJsM*ykUpBn6QMXkr=pnAumJ_9)(<9SfI@jR*n#S@x^H4wFULenvh$27F3W3-3a z$Y~hAgUU@qYc6sIM);Y@sl^+biE%z=p*<6IiaRt1qeD=+IT#~iK5{n3X`ynn(OQU{ zixELGI4OP$#S2=@%VnUo80`fZQ-aDZz{nCykn=Gf50#sb)-vQmjK4$W7NWHrxd`L) zP`O2Dtw1gnR`PNbZ)7FfON7o*9IDDJr zf;RKWCKP9A8>T3}&~~mILk&M1X;6k0oxTQH8s4$keEqIg2P zxxNFf-Dq#)@o=H>SANGuc=h$5*FDS8LR0XoXdQJkWqXdlApDhD~WI7J6Bw#Q+#529{B9>z!? zRPHd3`9X1wR9@}~j{rh(fhYnH@dTzQF3?F{j-mjaMB9MTgcMGSTtxANPGb$kQ#>xw z35-@mm zkDTVDI3yJR=rY&OVeZRlU&6>Fq+jAON+?d?ReoJ5h0#i`qMnE`OPX?C#grAQH@JQc z^H<3*-wn+=l>zJZAbhpt{5h>=073RIe_3N1X4%)Xc3JU4Bcsvv< zto0tR_jinpau4+z7$1e|y@A#P71sKJ z*ZUeHzI;IY6~=v`dS9XS3HcGDz>xkC=j}7{1IC0Q{R58WF4STbKZDXI*`3C!Zxm6kXwS!fT%I5WdIDdLP3s@Yt>jiZ>2 zb{58>AQ@c@!HfthIpG zdlzS=0QF}W*GA(Xn6kq61TlxR08>`@o+xT8#FQ1*suQW+9DG-YcD`UH{zNApQ>3Yhy&> z!Mp|0c*v*NJT9OUV)Hl~q$oZ1*oqq>_Ygl&3fs(M)N5}<@Ue&Mjui0|QurYw#PrEy zkfQrw9)$=$RIejk56}hieyCm-C{HjCqW_Se2f4jK55xi@-@}LsL~#Ip1R7U{ntTx> zh~nA#2{f)OhwlgNir{O{9<4cL|3A6MG#}DI9OOnG|Cl*N`x*Ulr;PjIBzAPiwmU;zc^GW=mf-M zDrzthDa@Y`p^54(YQ$>7JPh%g$WKJ>GGG#-Hahxd5omrr9<)leQR40m$rwBBz zoPe)D%R;i@%K}3R^D0DqBHs*E4m@QlFVMJh3gyZ}pEXqw&Ox7rUjbS{Y$);-j?YJpV|hpL)Eib#d|5~5O7H%RfRaG#GDRaCAfB30D|Yam_~ zm8$_&AFPGwRaCB)5yuMiF2u5;a)%$D|z8ghxQU?pr;~EM+);SAB3CI zpAIz>x|cA^sJ9o+$}H&LgxR?qj1=Ou3UiF@OoclKdX_LZm$Q+=JWZHq)H@CCJm@rG zzEN)))O_eU!UDsegWL^R!Ru(~z6c*=l6jGSi2}_La%!a!J zdXca+my40YJWg0<)H@gMGUx@ua--e_P|Km02rCSK339K1UM8$G{AExpp+^a;jO~oV zSy=_WSXgb;yBKOU^h9Be;ZMZbS_8dGSZnyJpw>b!71kN`E`_@edWEpwsCNa_dg#@{ z2E$*C+#8_R2^$T69n?l>Z()-$u6PTZk-|EUu*DcxNkbAonil9l~zI-vPB7dYiDvsCS#N7b(o^ zgndT6TcP$rZx{9(<+elZhu$R|Fv{(MIsm;_IB1mHE2JZZd7EG{%I$%&K<~qkAyBz} zP%5}z(2R2Xp)}}pK{v{!3mhrT--JU(xr0!Lpe@2-qnrimFtjQhG0Le>N1(ZI)F{V= zV@TPXbm6#BPKP=UeMmTAlsg1<0{XCU(kOQr>Lm0r;gnJCm~a{?%J0eMPus_*ano7W56_w&CA^x($6zxMS3NO}L8`<{QF2qu#4f_n@x}_lIL*;;iXaTG1N=wXTsk`xo5&Fq%fZoUK{0} zLcNB5F1#_yJ%@S&{X%$alzRd77W$R&&M5avc#jn30m28P+}}_ipx+B04gbCH2`P-T z!e_&O2lW~HweZEL_ch!v&~Jncquw`A8PFeuuZI5txxYex5xyDz7pQO09fV9{TH?=y)`hHndn z6ry@L8}*_LQkXx9F1d6<3gNs&S7SR8Tvupk(JhxQNMYV2<}vDZ6y1@+JWb4N)awA1 z7usF)Fno8>6DiE2L@&e71LXznBzha$af0g&omce9r593|hl#$%c3j~4LVJsTx%5E_ zKP)Ty8})jM0Z3t9CI%YydO!t22Z%w2A0U1cf}s7yV8i!^9}Mj!h8WxNf*S(uBZeCF z`ap$32Z~{aABfyx&>>>D;fFwlLr)eWL>gBn<8(zpuMr|e8dui9kAxP*D8m=gic!!J zVzl8$Kt)5Z6=FmhSJt9j4D<#eR-|!d1N>O%NHNavBau4}I!4TA_%TrVpl!tbM!hyT zEBT>a#rRyhA%*w9!~({4qTm*Q&My|sWjs=tr-_A(dZXbMf{qgl8}-IP6^1S#7BTz+ z$Xx`wkeFcjg`g6k-Nd5CcHD4QibDH}#d7I~6y|keabr92aEn716-(r@7*d$WiHSzN z1>q(_7ZFPu^%j9D30+()W%$LByA*Vym}K~gP)X2!VrgSLemEjZwa_EbSY6W>MaGOK$j7d4ZjR>Cqv8l5jgT?Cl zkx{Om*cd6y+r%bDxw=qIpzDiGjdJy&nnE`an;GRAKsAGIEH*dFH5OYSg?XFU(kRym zswH$2v6WG-2~;cSrebTOTvMpl&@IF^M!6PZTcj|*6WbZ(nnSgNZY#Dod;}yz3gfNV z!SLHab%1Uub~Ng33AZD3E3uPNZ!4%y(Cx&|hTjgkJ41IAyBK~)s4mb&m?wf}w(T{b zOTV`5yAJ61ZyyQx*P|Nx;Z5%V?7{|PJp|3z4F znn=@RnI^7|J+OH4sN39Hu(W=X&)Llyiag*`pmd>eTm!5SH?}~Tij5-$L-=r+){ov zZWHNNFcUX`-*HoyEtqh_CK|VAwxS(ws~m9a<%HWO7YxyExEXTCt&Rt7hrDo0u?Qxc#6T61wT28wR8WOl*yxy>p%ZOkbg`|`UA9GMNZliK zfz-`)6}ySu#U5f$v6t9e>?8IS`-%O<0pdV$kocQ8SWFRzh(pC;;_u>cafFyEjuc0U zqs1}eSaF;kMQjyPAGC(aiahzrF<;$m@$ zxKvywE*DpbE5%jfYH^LYR$M2p7dMC-#ZBU7af`TB+$L@pcZfU1UE*$WkGNOdC+-&y zhzG@V(ITp%Ch8&=4~d7xBjQoI^kiMPc&;$88ccwc-V{v|#XABm5}C*o7_nfP3MA-)v<7GH_4#W&(x@tycy z{2+c5KZ&2kFJgxHRs1Gqido`!@rRf#nj}FIB}uZ8Y$ZF%UUHBeB`3*Qa*MsqD21SCx-4Ch zu1eRW>(UMBrgTfXE!~msO82Du(gW!)>7n#UdMrJWo=VT8=h6%5rS!M-N_s85k={!0 zr1#PX>7(>X`Ye5sGNiB4Hz`xflD1=l`EJ_~$k7&uieH*T6ro zfqz~D|GWnNc@6yY8u;fm@Xu@DpVz=YuYrGF1OIow229B*_;O&Ef$b7ZCe{l{cb1HE z9$>m|FY$ok?X@61l#OmL@&KD?t=PLL*1%ol8Bf2P3vP&F88#xXojqDjM{GA`q{x@= z-z^uZ8Or7rkobdH+vSqS{n)Du5??j?k~wUSCku+Q;T!to*JAG=u6SV^Zt5~aor5@{ z2|Fb|e0oPMva~<5tCXd8%81b-Mn$o$Q@`t-`gPQt>xQzmOEdLZ63DKBgH_=kRCOlQsGw4YU-zi#VassGnHli77FsRWoFA9^h-9sHGQ z+9P>+aCK$9Z8I}5Gkb*WbGEl~`I)1cp1%Z+E_qg7iRVX8tS?tb{!!p}bO_mt}E-}X1dCsKWD@I2Mm4CSak%18B4d8#ifdxT2m zw%InbP`wwPIa;V*B9&hTpUP7{D)-<0{%L?{!dpai5sCP`;J^PY*w62u+TuC-Ua1`( z>HDA#2I-rlF8)5bN&%BWucwY_ zk-(Zxa^^j|4ptM}CNRGQXFh4_6m?ps1m?8Mi4R#mPj%{%z|M|z-5q^Am9$)PRoBEToJxZxJ|FJ<~Os-Fe`|iwvBt#`KP6 z=Q}v@unEi6WdovFT2V*-*V=XJJ^X4<(F=}zN%T5(XG%1CSp#`|4_V5ziDr9uJMxC9 z8!f)=t@E!MW6$;3-PFD#qFLkp_PmiWL7h4>n)Oe$=OItasketlv-r*Syx@%>HUDqX ztcRTg_o$v>x!5*vTjP26lL-#MDKTIj$F zi7VBEsn+>_ZU0}l`|G**b>EWN1iaXbm@@Wia+GZ+r9|r+GSAydxf}FBPV?`ir1iB^ zh@I+qD&*Tlg(;E9_t*0R${!gWl{(EUDJS1{RCcUNGProp6!{diTwsk%&+BQORC+#a z^a9Hvl)s;FE}iP@Tl8F6V#U!5%;X18H_245%XE`UOj}#bD1SArlTlxoPkm#1-*2@x zwom){x$k*1?f0Xr=gqXg{fnJ9)BX+sX@BW?+TW*mp7wWsnG0sx-#sZ8%(TBm+Ru6L zX}@r~O?3P5VpFFWTBe zq2nYP^-WDN+O;o8+xhQ~7}anWQp4!6Xv8oXeKGaWIU@%BkmUcqPq5NH>FSxg&B8ni5Tz*IqB`{~ik__6w z$vI9NgEZm=AYUL_mPjKW9e)r~L>lpez!0Q}ccr%k9;bJF>dB9G!Y927VmQEYTB;1M?w8EW$RS zMgD!nP5igl-2eWF`+xc)F?CoXP9L4YfBygVHZUch3pegCiqaj%A*9{eJls~EF*%iY z;iCIZ%U&gcZQAV2Gn@xonj|H#9}8Ugs1j||12q%ag;_3q+si0bt(3sNHgMthCr?p- z)K6fI9y;;ly${os;t6cwN!*2)R;#aYM>4_AnQNYVRVUn$+zfHzdrVw)Xqmvw`5gJ0 zb6h>&+}gkT#gX4XRyzG<5!|WVwd1$LW~dUj+qu6zZ@qGz+78?D9%siByb7x(>}UBY zcHFjAH#Mtb0t-B2#~-!-+mc=?fmzHByj$1(>QL-MN@IJTGGmhEY_SA3CBT8-pI~Fz zhWbm3*w&Z^={pK1u()+jTz{mLIrw`8wl3X~cd4_aY?mPwSf|^LTyI{dOla{W_T;z| z-!{ETdjFD1YY+{lNfA2L`E!sGV^_=R=<%+Y_t}80A_$|(S>cqurc#9;~?6eaf*<`Ic zesu-r_tJ@9T(nh{TPLwi4;}fCirdw(YbvmAk`q5MaldMAlf?48M*Dc~P^%;VSyxBC zukis@LjGx@123>;y_&JS0;?I}z=yowu5N9f#7dX4=Mm55srMFDU|-OleF`jB=QP6c zuCU`vI*wFF%)#*xvubvH&*D7l zun86Z(>$xY+3^N3VQO)-+pFnz{LP@vmQrZvjoa*a{t4$SZ_6aH>OuD0p{lQ?8qSwv zS$jULdsB;m^U~^`Jx{%R#n9Feo-lgx1vV~GAuv1SQxaq{D z(oc#evByqM{PvSR=E@C{ST(aVcNpt$UeF|oO|0O;Q%A2mSQc$^$Ig}4elW%|zH<^| zeO-CCSZ8%ZpCncz#g!}7ny5S_i4_fT<^C}<)Mq1;*!?apyxEs+YW)dG%&WUIPaAqd zZ9652tu;IGejTr<8~;dRfj=C0yS`UdPxyOI+w+*Qr&Voy5*vKPj{mk}w;GIkB1Jo1 zIB2f==inr!&9&w8Ms`%U_D*8Er`dAe%1@ow$=W~v!j>=GzsS#ZM(zxpL0DN|MPu|7!_0vMR3Y zK1u0iOo{BOvnww$euib7Ln13Z$dxx6l2=`yH<48s?#c@dFUgVLTyR76fWP&EJzuir zoT|npvOXtqu5ayAgHTV9y&Ydyae;c#KauTSWXmUA?yhcePh^{C+43FRL)5wU*8cG~ zwtRT&^_D)__}TH*cKmJEb?LS^md7jX`QRJp%VvBm!RlRb;OolNHgEn=f=xf;#4qRz z)3>=MGWQ3LSaXR#*wQJHJ(=Uo)7=|bPI}-N3%KwO9w#jMun$hdUHF5)1J%M|iLBHZ z7rryGu^Jzn$co7>JjG>#+8f7m1v4@4k;~LpMHAVfH%`3CtR3pm5;$H5Cthb{x;i8= zk^Sc3$dhYZ)MWTyVjQ^mVwd^~KXJaig+0%RT&4b50NXuo#|JE*q7H~jWWNuzYeeBBBPJEza9qqGTh^;Q=$S=p%(FUz3#5SS-`A~eV z8nUgpmH!+1lXV-{s^bO}XX^_&@V`zjS2wmP%Ip`XC9!N-BS+rKW4l@|Es9+Ub>Zt4Zc}fbjbcmgJMnux>S-f|0&Ho2oUh6C zH1T2lf9ikiir3PDqLvy?ajgC|XMVWIWy?a(ICkxCXTG<>BK1vt91G~~!o$1QQ3r&@ zv7=Kkt88ph*C)iWmKPoPt5d4ludsFcM|*qj|62ChK#`B=dfDQ(Ac(DMEAp>h zebuLHgW24kB7akBuDWJVF#D~Q$k+95r!MpgvGSFPBHz<|UonY97pz8#?jEUmex^bwk)#yaq~6*l%&D5X_3Vb>fTXOjnn-31Ob~oVZPP z19i3Z9IwzGpVAKWqMd%M){*Ri5&0$D8K<70S!2SDiLbiL=OO{o5&bp0AY?=X6k( z-kvJ=_t_*zo?R#R?yz2VaJ?uu_h_RGN;)K`O;{_haC#&cThmGz9dJud9k@mo#(b71 zylJUCo%KQP?Y2f9($xm>uv;qEKiMjE&1>Y;wXVvO6Rngf@3_Bou zSq3TnzV=r1b1BNY(+A}@N|?FfBj`e27V=GT$I{8)6L+EPZ?g}|dV`1Np?iBNKiw8|M z{+WaF{n{Z)*U=XF%7^^Qa~oZbd|6U?Tlug&DYT-}Cg8aIvRgf6gZF8<`P$aX(9P%M zD{2qref^8_^X)0hv&EO>Z+$M64R3r&zJKUsI%%&r3(cR$UyzS@iE`3_vvQXmiE>8T zN%{1+?sDV7M`f-0M0r+3V>$NAN?D4kC$C9-$hXCs@@n%3IbYuza;LR+ z3cpuFKDozJ8C|}XyzEYx5_qVN+&Zw3l5bW6d0w+J%BTxX<$J$XQEnu*ks9g2$ zEb~ikm1RSF%G-rrN~hZWdJU*{O{N$YDu5opjBvQ_Vi%edR>wx8@hi zy33D><(FeD9pqAfwv>lWZz=yhc)7gwLcBa>&N%sPLV|pFdAfYsB~I>i_L@BSUbKAe z_Ivq;N3?wOg`MIvC|VxX$xD%c#K>cxge$)t&nLHOU0BgR7nGaqDywXsP)x4Dt11to zOUqxoHd2=OF?syzc1mc^%5uAry_8e&HRK;bLzE6_wdC#1c9l&nT}w`Glb%kxh0`Q+ zczkvFOYn2^fa?|IhMS`0FL@Pt-`pm0wOb|SVQMXfuG8gRn<;d?%^B2Dq3bf%y^liI zsaMA#3SF1&+)FFTW1|%5Y855=Qk1ekc$s|kP*dgO+7>G5e4aJrLk*iLpJ&vR8!fD< zSgtOX6GCb#N9xT`2Rh_e9A?*6ZAt*e z(KMAf;Z!Y^{E@#i`ETbY^2R3%t(`fs$$d@-r4E^LjS&na!;g+OD}>cZKq`_&d#<=;W#_J|&w2 zH-{-jQc7FQ`@)oY`mFCxO$=7Q;DQ)&UD+63Yln7gs;&>uj zN&e%RO8QCb0Saw*Q_M4!>bE(Vtx|pe-4)4kc}<9rLMsonA}L`VhDh%T2_UVE5$!-) z_o8R$T`XFWq?JQjiKJ(2KzeWK=ZYk~cSS3c?#9|y&WfWeG_73ayf;KqPw71@FVscP zJK~v~6-n~FjWtPH!Sgm&^!!0u!K0N)BCSl)N?Z^~D|W$fXhkjrBtH}mtrUiVy+x+7A=x?m@;6W9@~2d6XG86>|x9MWBoA}S!YQvms)9w7Nm;PeE0g5)=aL%J7I#AaYGus2e~=3sBI4^qSyU>~qAQpA>EU$7ri#8zNG zus>47)?j~d08+#@-~ezSQpC34KyVOJ#CG5y@HeE0?a^w3!NFh(*b&YUa0p0#CpbgF zp&!ENPYt&nk&Uz?PB;{k#dIL6_2(cMSRVnt*TU5&IggMm{NOx4uR{Pi^Ob|Nd}> z8@_|V0dPhb{&0f>(Q2vC1E7e5&}t*Wfl$QX;EXc-kp>5&)!d*{jQ2XI-3A+a2p-+x z4~3IMdJl`*ZD=m(y)0_CVY&1)#*uDtyx@1kBemOTL}wifjzc6@;#hEk!3jp})rnA( zpeN+UY@L!Di**Vjt4@PI9Z^%$45k^eQfETVf}V-T*&sz)odeE76jb6oL_Hl1&c}9% zqp`(_;6y}kB|QbT5~rbtobvOG?aYHq+Z&HPrTv?X$8q3P?9Ui54f{dcor}jA;23OS zD!2gAQWt>>5h<0p2+>rR8eEF!5ThqIw(4?3NnHWG0ufWmUxCME;A+HBT??*5intz+ z8^Dd=8gLU*o53we5h(&H`D?(9-~!aQ)zAwJE`+lhdNoM-7GuxWf$Ko>DN-zLXDvwn z5;*I@^&t5aIhMA+0VIDL9Map7BCf>oYy!6+MO*=H0XHK>Tm|Or=PH9ca$~LTM5NSR zczzF}r0xSLX6g=*BBve%4!gZtndhOgl$ zh$lhX4;3k5I*#NNcp53y?H=%d-aCZ+}DAP5(Tu|Ec_+-md>s+wJFm;iZiGs(<~TKJ`QS ziS++pZU6tuwkR*vPy70_+<&&cpU3m_`JWmeXvAHi33L2^|NrR`f7bhxKg<8*&w76H zSN~`FSN^Q$=Qe(p`;Y$ro5pv-aaZ|YZ{J_{h0_0V`~Rx`H6Nva9*-$`7-Wauz5CIp zIr<>&&bHxsH&gja?%cuurgq+kv4Ei-|CT0yP=mVauCK|4|1(W~So~%sDM<+Qq>^_DNTQMOHMKLm+=kpt3M{wciq#VO;cuY?$S_$$5!Mun03U_;IIa_ zjB>O5uNz$1YretNfxZ6Qt6nZRjegjN|MHj_F^&i!&u}?qV_xNa@#+UVF=tc4r@h=-18h zi|2Zi+Jja1%y?!~QsAZ##S+jlo~h*ozOU^-b<2f#c6G}SeO|$`ma|jh*_sO?U-hPq zrO@L1?AtAo-@e{o9U#QBaI?TaR&$YCHO$ZE9sHqpxZ*CC9F(8AUPYc4@6D0R^0CEV zCEiV(YWdtCA8TS`!;{YTS37OV$BK-%;h$UYwzSz4%Zkjf;pH0-R3~S|vQOV7o;+ud zc~$>7R(6sNk8`YV7GA}$zV!t@T3sY>-5bYxKmVb>dbmuke=d&gcp>tViBsk3A#p6+ z-G-0x?k)QU#wUxC$kCf(n7^yUi@mxc zFOH94PYT)a2L<=ZADc(BT{~@fv5#r8sd5x^+#v8izj-MYGm?(Cv zjKm|-9h5w`Bbj|u8-DnYSMq~Ok*xf69Lq6Xo|F>7Y@Z7J+?)c+`!3;Zk+X@9dQwDj zXc*4S110WKDpXleKActh1K%#W?4*qSJB$U}+4At$f5}PR!`R${A|JlGj54He2%A~o z#50;CD-pFqn7B{k>kng z4`iKA+3_<%8`5l?fXH+1C_XKDqD@xuqJ&UN*Af zX}{gGbblMo3b(Q04dlUU-NG?e=PP=M#NU(>)zz{exhn-e2hXk`<3-r}qFyAr|V z+9uxM_DFSHb_BbL{C>kqSbSbbuq%~JJZgPAOSPU6>{JIEzU}L0^UT&!R_FTnF%plv zJF6#4y5q2@D61X~d=@~qK!%tdjI z5^I_G^ghSRCY}su0WD4ZQICD*W-r6oYP_ykotg6GdEr*)I{$WwukmayUpg1Yg18Ou zK4z;t^=hcqIqmYN#P>fKBd@L&iXVIu`KA>ya=C;swjb}huRBspet$KDHSKTW)tvIm z_pXGp6VpxH=4hh)Vp0g}_SS} zK^yLWW{$jLQy`l&UgVeibdt?ggPF41#Do5-A+K-=W-WG!-2H72Ibd=in|KG~M4uFshb2R&QH%p)1-o||6 zUO1cfBTH`}UN9@4!q{)yzUpsR4>p@|t-ZU9^ddL;VfS!0zvCDEg6W#M@32VbTmQ3O zdP$T#=0X%}JmZT#E3le8xl9B*IR3Lfu<|5%M4d=hz2JBK=7UhV_Qg=9cKfbhlB&qr zxV|npe$(B~*OJGEg|V8ozUeoI|1LLP6vAqs|DrqAn=J3x9mcL-_^glh-zPhb4rgUU zGW25O*2+CTgs?vfe9=3yoASN^p={8e@49JQYq{FT0p(hva zB*(@FGCL_#KfQjs+~G$6yD=<7pRsbEtb7k*S@XW=p=I945k-U9U(Yl2&MPhQ_FDn0 zqF07~cGM^N_VECwO#7_Y%G_+Z(l?InTJ>4K@@S-5@pPP3JD;kOp^L&q%l+5+Sl`we z`a!o;HKu-k_ImDTy~W}==F!t*t=fEU{S19m+neUA@8VcJ?@YZ)cr#0^Z9F?(AXA?d zF-$$!$9jzTbxfHWo~cJnvX#4k%4an$R8Ie_d-%7OKb4HJIxi1!ye~Z`$lrU%vI4a- z^}ts1<@CnZ{rsh`pY^qcx5Jb3<7%0aD^HFGvt|b} z^cD`jN++Dh&6~dIA^i&}<8K786!E)W3J!Y}2gk%O-_4#eii+ta< zB;`OAKh`qC#Gf^jm2d|?wy>@ZFWo#|F)#LGgCiwg?QooOu7x)%R!iVNvI{7luHNk9 zG8^7#UZ67TrZ>CQ*p}a&=&d~b=Eq(hu;nGLf0Um$@@M&%+455(?UmjGeb~ZpwtTkD z9l1-8FPn|A%=Fw%8N11Yg{=^H75u94!=FFNO7Wn~}8R}baXZHHDE#DaZhkDcE!g?IA;{^&| zvb-4K#wNYC<7toITJkCGtj1+Kd|Gp|da$A!t7(f*(cYe@es*(b5p8UF!Pgdf+ERb3 z^RfnG^u*rN<#s3hSuuPHHBaLWvbfFH>Ktvx{y5f3l^uQj*tfq#Ud6VZJn5W2Tj?rr zbzv2`$X0*G+$27#Md!L{V& z3w+oTydGl4RhE~gdRd*LiH~h~k!?Hhb0ev^sZr9Bp}@DXq+tTX?aVRqS}! z*M#(|eLbwskzcGWU-dS|a$%(>t3O!c7c%yi9hvFHMy5$TYt#sfin2cz3OsqPQ`y2^ z-t5pWf#2Of%kqA>7rW5Gj@!pOstzynTAi04vMoOt>ZiUg;mHOJw&RlTMD^m4yv(tviAH#`^hH`#3LlYmdasKbWZcKlNl?uHjy^$z*j% zUN5Hf{Gku3&|IF|%b%U>lBt_(PLjO>@Cm-dKlDu-6?xQSUv~9Graq+c06D$0FRRw# ztN!85Zn;;wAB&rlp{KzyLK zWIfG?J#Ct;7rDH{9FyYB?uKXSQ?4_){@>ngTE8s4z_l#%)lFV3tmrqr<(tuRVQ+7i zIPI&RvT&>1F3O9QoAFKGHL;{Te6c69HGR|XdXA9qcJ^Rz=c3N{Bg_7=(2LbPldVUT z|HE=R%abkGpQXDGDQGrt@noAnW$6x56U*pN9&GL7Z~B-G8_gejdaz?1zUk9@I?Elh z+*y3VOx^GP_4Hj+^Rns(zUiqQUX}UwDi1S%&eq=!%CGKOif>*=f7csyh*Fo2_h6|` zCccUNp~j5&WE(1f*CUi^>Wr=)Y<0&>eQ>P5`oumj>-_zjUg3^u;%Q{P&gel8Sk)&c$l4@A~L2hvb0n z?rf@$iN|idCP$$Sf9+T2w9eF@v@0hUUy{eF{e$Ly*N4pLBdcj{c#WC3wqSyM;)API zzZ!BlQ?Fw#XkOaJ&FUO^+WpY4Uz=%ORmhbspKIa{;ktRxKwtJTD7WdwAa*?am zc}Z>YLtiIfw)|Dsg)OR(rJt-hQ(e%`l{K0DL;q5FraC2$3p;w$#P8>|SYjtw_qWd; zn)syAX==S>>pu4TLK|N8?K|~`vkNPOF>&XXPinC-&Wz#H`q3X>s9_PV?A~k}o}Lw} z%?@;BH&2Ls%K7}-$ptR#W|l4Q@+4f#AL_>J@1alL{XmUh>c(p1vFDo#UsJ=IyR)q~ z?fBiE{@U-Uc^GrC=bZ=S(NY8QvgspicyiVHT7~^??Dck$=XYtWZ7%1=N<6dWajU9n z((*iP)CoI2xQ(ni-*9Jv-|hGzURWEJ?7{Xlv*8oQr)WFRxwAr3MBXF+aP3wHcXsZm zEnhRDm$rRoUS?Tu$L)5s(hAP=U?={vppGPM8NZ z*B5FT2ff*bmbQFKn;F`!p+2ltbvu54?>KFdy)SEnZ+kpB&|j;v$d`3pEpfH-GA$+B zk9qzn@Hr_fwY!J>*pmQTeyjC7EugADd)i3in-{Lo{_+lDVWS0}_wH)#4RQZk6iZg#x$t0~%|IsPoJ8QP@eaP970f0lC6mRGqrRa;Osh>Z!d<7YRgYEek< zABj)XH#w{Bl{~D@%b_N=e8s1u>YM7G>>G}^=p|b%a=C}qIr2oFveUBE6=ywJm?rUF z2f2F7(~FJ2C-Al{xjMCs7mI~IB~0xHfH;r`5UZ zUdxuJMfqrl=X$YkEpg8fo}>-D;$?M?3S!>fo8{MnS9`O3dnMlEzMB@C;LTcNycoV9 zOyhw*tj~3NUW z``YmuEt_exg8i({QEIjgfAO`Vwtj#g6Q)aiLs}tiyW-0#_r)A}VJS^m;Kyq26!@{* z`L*yezU=O7fe)x&T=N;{$CB{6&3f5RJx=x_q$*I|KjWn9u5q2rssx@pt`56G3Yb>dAgX zRE<%MuBfxa^0Jq5rrzcDJ#|@!JnZYjOug1cM@>}ovMZ%B^<$?zwd_b-M;I@bPj}af zU-V)Q7*lpsi_p&DbzZN+cYR+8e{JA%Pj)@@yFRC0LG8k4FSc)VrrtFrTI;#pgG~s? z)WiIWYgtwEvTuzu_2OY>t^RvY_7G!P4X-+y=@edv$Fudj<%($PJs);sVz%D0dTGtA zv>*HWB1;cyZPp5v^I^l*U`%wbsx?36%S6=Kpj;iTz+i87e9gSF$UeOS1ht#?UG)(+!S{BiZO^dfe3w20yUY}TV6ddaIydonhV z9eS6kC!6YNwK@c{!9Bj|elI&|%ZlJ05A))@l}Bo=p82uf*=?p?J)_>_IKrkML& zAEm8C+M`*PerD%$)hXT8sy(we{?PmSyi+grcVX@8X6czdg0&Wl-K^R+Vfhbz>X#_( zP9Imc;*E)~sq;yFveDV9?;ZQx#NTepr>!`MKK7UX*0ErgKC5*(?fIuXR_*-sw;y_~ z-8Hq{@6mt$Y2pLEHqh!eweD9(yvfv~wztzL0UzPDX@oM|K7JACls{OSq-}U&7DKLYHgfmRk3>ehstr6a80euXhJp z^|8w(i8rsgR7;8rWmO9aeCm)D+C7I*R&%5ckL)*B%Nr5OCg9r`tFF$`qP-$m|LZ31 ze|En1N{V0~60mkUeVVraQ8+u9YRiXsOwr`8AuR2dEq}Xpm^N=)2pdw$miKiVtL+#b z&i-0x%R`R!(fqJJ*1aXZP4axA*6T;am~sYvGB8g+JVr7ih8g83mz&#~V9wA+!ftXl~acX>5P>sut2HCQC^0p~hvPgcjU zuG_J0Jg!VnUQvy$WT1_hzoR8(XovmjLtED|Dk&pQ`mw1|5 zPAfM&j@=q$!~ZT?QajTxmX-f#!{-jnr!De|VYbr*-e5_HR&8N^_D8#Hy^T3s``wbC zEy8@ckhj0q;;($nx1tSq=#^J%yDg5Tp0wdv0~|Cv?B|0v0)O-7j(TH4JPWfylpgtk znz%ilb;=NVden6_M9Xiis~^Z+SEqH7X1Qzcw3dE1cP+hChz)PQ;euLnKtA?iE!J;q zolu*cz?vA=%uD}VGyi3seB@wTULdWlme#=fyp+TI^paj(`}=b+>%(mMl4Fgv&*PFoTh#_ZOa_`28ttzLXMyM#6Q zF?oVC*OgIL=kCTyiC^xUS3BG;lI6ubx80g-wFcsK{CbYgZ*`vN#OSgTdx}`<1%l&b$vP@S49Am7` z%Zr&3FDo8YTThI#S|_LV@Cmu=;p4HM{3>IwTCHy+`++s&El&H?ug@Zkb?^SU>)y21 zT`za7yUF+;`q)548&ocsnJ{O`xL8YDQYDz3Eb>EdP@7-$I!E zA7AxqGdgO0`v$W?*E00j(W#p6fnfGH7@*Rvx27)I2=541E$+K+ zSK*afZdU{=T;`kpZIqWb5a~dSL3J8ss6~2WeO}7Ym$`*$e`}HKhQk;A^XYNm~A;QLO&X&w8`dF51`vF{~NZej7DCs5Z(Q$?BZPT67dwJ#IxBYt)gsYt*zZ zJtKErdei7H`d>>KqR5nXLF}H?R(Gq9XuNI5>qDS@g)|9icR_AEX>kPd?OpxYQJRfU#G*h2Y zD@S}!Xx(+yX9m78`OA5!IxjACCGZe26= zCUqXDPvWfmTUwW%lDjVbYag4;>Uh$7o_IUe`1|zC)x5!XmYD>RG^L4Op12zCKNHW! z{$Z}tW|o;q&lAb75Rk{r=T|oJ5lNd&sfH#}zWL`W8zlYSa>3lKLDKJpU3dW zUaw-1@|C{tDDU`vu9-;rh+nF?8~(AeqwA~y(Ro>{^UZ&$DQa)l%JEY1v?ey=*Mt<<+b}}6wk@koFbJ`;(y}C$%x&P%A zMqNbGl!v(GkHdyfEVgd8Iapg^ke(+^{)l-&=ICh;jC@4W*e&Dp0z}HU;%o<4u)w+QDdlh1%g0*4FWyiAY*ai8M5k+Qo5-he6WR-hH1W7|#=P&O>r;J0=_X z)_zDZXhM`dY6sh^9tyPsk=m*M-;stUQoE*`iyEZ-lrN)dQG=xExTzc+H<8+f%8@2g zJ5v6fc1<>XsxPOVk`+2`+J9osaiz*R$D3+sYB$>7oOYz+NR@MrkJ=|$q3f`8x4Oph z5p%A~9RJ#}wuVon>vLM`x(2BnY4Yj%Pdnbn$VbfS7g7z4Z5jD49_*7_E>)rHI6I)O zL8^yz|CVhHCj8bX*H2aGIxg^`jzP*tT8?aMuyyS|xqhlb*DdACxqg!kQa;j@FZa4u z=(?rt=3KwY25GybX}h`CwL;e?9cRvUnrtwq9njYp?UX8$rt6)KE9d&B<4u)w+QGP< zl@QAeqaPsBYmZ3%z=x_UjedhjK9T$?4`&$t0g>t=pZbfOe5vxV!nzqi> z6Y2VQ?Y7$J7l`B&$#LE?_`Fu6nUCGI3 zQerN>HI9o&<%l``ok__Z7ZlPd)7~1#MWo}T|J;7xNYnAt^-rY!fR3N~2O?=|N4oxV z+BMnmsej06r(}hWllmb#P9n7f^+TkI)Na)Oc1Tv}xM+WgIsKVQ$?d-t($p@rzd7wh{h3M0?XQteR_OYn>on&& z%F%Sa(se}Fbx%k~oa-yuAoUxhso%)G?i9MN z;?WP~Twmxnps61sQa_aArz&(^QNEn(3&;Pz*n1B!D~fJixC1aSq+xObCJ~3s05dSV zdp9}fIOGhHlYjvRP{~OU5s)NF$w&~GK!bot20>8CK?IQ~S-9_8y=xA7InVQ*bHDrl z=ef`Lee8F=Yu8#`UDdU!yL$F2aF!#ooTE=RIj(qIXMDK@^0?$Yu4mkt96vlSXB@c& zI`K*|J#pP6=Xm6KIpb5rXVVk6(Qe7+^z+%pI3=P@6(Yy!vt{{A-scdhCsM!cUUo5F ziM(E^XS{OCB^&8Mp(f)Mk#afvKP^Xj6R%h<+3@}%hZv_s$vIxLZSggEA48;`Nd1HZImEan;vgpVj8{%M#LT}!P2M*V zX-8xn>5wzpE9At;;4tCWDW{m5^A;C&MA{K)cQUf9I1Z6|BK6y} z;=&I({Y`oy+TC=@DW+fVcyV4t+7X?2<@UrYx8NzY;>CFpd7f-HCtiv6HF^FV-$agY zo#+O?l z;}ALHkZ0VP99Jyoj4!u9mLq35&$u%=u6SH$e7Obkxa2&pXWW?_KRhpI9JvKL@e22N z;{M7l;uXgu&&wH~$)eqyc*S;0HfNl#EygJk*Ofwa;uT`Qh*!=%?XzV~Jn^cwCtk5U z<5d$+yka@Vt0YgnN)rCaop_bxiC5G!UUe~fe?{bRiB7!g;)z$(GhX!;<%ldtbmCQS zPrRa@@oJDKUJVlE$enmK$P=%qXS^EWiB}^;IdUgnC41r(+lc=qd*amyaeiJ~Sl+oO z^*nD1?)^m_PrRy=!JT;3)Dy3o3VpUMO+E3djwfERJmXbUPrPC|#;YVxymH!!+=*9R zJ@Klm@Yf+{S5LfZ>xoxgJ@Klo@Wb!vZ*$(aJn`x+VMp%7t3l?^h>Is9opvEQ@#-y4 zyc*<*SEAi9PDQ(7{EaXRAPy7R9z-WzC41skvL{}R@Wd<8&MfPRSE79pr|Nj(RUJ>f zYU+tsO+E3djwfERofxm0it$e*j|^_>iC0}c@v5ySUUl`vt0YgnVmmTkbrtc)X&1(; z-kx|h$P=&L^2Dn_o_O_^Ctk7L7_SBie@;6vUXAd?s}Y`fmF$UE$)0#M!V|A}{?1tQ zJTD5q>|Sk8yy7@wys9n6E60-)ubK!wImb1}CC9BZP8qM7h;dD1+j5*a@ya=0awlFT zdE%A0o)C|^n7nUt#vS8T7t@JXT|Dthj61}m-lCi{?ijCni*iIKUh%p}Mm!qiiC4Tn z7_ZQNB3=#h#4GBP5uZkQ;+4}L3gU&7XjSG;bL&HrvTlE`?B z2KvS3$iheqq61NiaW&lf8t$rTCBE zWjOhcv*6bk)H9a}e}|C+zqH|REpmYISLw{aOTSL$T^=JJN)nw+{MqqablPQyp1)4d zjo+fvE;kr|ozC1Qc?B{PFuet$B+(xdvz-J9yL@2GSL}=5ZP3maEEpJotR}PzK$eaW zpg*#j(9Rz$3>b)P9kdJdWIG8$mJ`|qLC^fdp~&7ryHKSdBxdT55OyJ85#V9SdJ--$ z0ur- zibB5;&=vzmBFjgVKqp&DA(20YSyzfeVs;hg10g!uQ*wE-r{ofORG1y44DzNBOG09H zm2!}Xx3O1(t!tY#~lI6=n^o1YQQ&RH!e5(h0x{$PPk%1y7ch z^2n}2zvYo7qzbScva8TyY5;c==$+W=S(**_etFS4i9K~@m@t%K|zjexb0 z^@DzEgEav*M)nWt8>8Qv0vjRg2lb7>ngQz~pGAFvO&~EFNekh(K3EIzCdm3heG`;! z32ct6AJjJoYXyu)_K!9KD?ws5kR;^yaI%6V0V^T92lbWEitT`{k==v()?mzz(pKr9 zbQG8biP=6niL58g`q2qI30XX-PeSP~zz)dTL460XuE5I3>OsGi(JS46)sV%*!RnBh zZKQ`dPCICOfOix5J&4^QG5bg_;kP}$?FHTi**oaB3s`Sp50U4C*b@@7fiS-Z@hy=J zr8=^C@Ho}cD}8|tM1BuqLy--oJF60?mA6n=Z+TV_Lf3)wvS z2plM~q4Y;K59<4)^kCpXWb+s#aInaR(hzw(`hhn@uM7qDLmm(M?FTjt*aF!+sBeMZ zdK)+t`8}u~3N{=#0NFg~cL1~_fP<0eV~9ZJ{a{uQ=KCNHhr}!@?+X10uy?^bBfAH) zqjXkAL1MNKC#wjvevAemg`6VPk3ueyF~IkbMT7eHz{UZSktgFrfulthjSod05M}}S zKxEe#jVv1<;QP@iJpuRu@^pM8(8&%m7P&#_cdREn$QVx+k1^0sfqopabI@)a*i_&I z?EIwtRbHu8^>qhvyp{^cC*3e z1Lq*?2JPm6EdYLstQ@ra6zp^0JY}JpM!oEfGq8-?H1$kwqD{3~SXSR-&FBxXn1EbP{RZ3bVDtR1vl zFY1{agxyByzd^aLkz<5*UxRH2ZbtTwEdrUBgxNoKq9oDDPO=sG zJ7~8R`c&XI$ooONZ@|)k+mQo=cH6<6Y#_}0!K@&*$o9e9AGBlckKMpj5urZ_b_BeHa#YMKC6r^3n0@NFm{*#D9S1+A zoDlkB%1KCGkbe;RqhLRPH&afDd8HY&r@&h(r^UR|66`eiapgy$KaSErf}c{(2>mIr zGvFtcv%>F5n`|h<(|;r2D=A-L%A=Ga|7D@;5U^A!tYJ62jF*^b;j<%Q5cM(G#e&y>G}{u$U`;AfPV!tWXM%1iJ|$}10EhUA6s z6qUz$0c^!oyO9b8ef2t5iz!gGz9Rp>Ratl*cG zY$}g)8NHGX9DSrZh~5EzrDTUCF`Jqlm`%;$K`%&n4pO~^Ulm$!@GNRh;WrCdPVnq% zE}_qk(z(FB)!ahw4VD}HcO{R?QS%9X9@PgD zo}*M>;Wr1gzTml3KjAkQm>+mv)nDlIqO?D_uNolqzF-01?<#>R=aqMrAT-Ed@JO|gu!~d+ zL-JB0)FQ$z0;~vllv-5SMS&Fsk5-EbyJ)at;Dy!V!mh9y3klCjY6)Ri2&@En5w)bS zD*{#$yr^1A*cAmU1sttpEwnS!zXLR~oD$cm=hR&{sfjRRWJwfokYL+DMEt^r<6tts@?z-oe9YAxZ{Qfou< zQrv1C;nxjT2fV6USJ+hrs|#L3ttaeifYk%9t=1QIwbcfY@Z6;~6n3@18iLnR8wtBQ zV2!}*s*QzRU9iUB4b&#Wu7TPV5}u#bX2PyMSTpd3YI9-N5Ue?PBejLFYXsH;ys6q! z*fmvKLBeyE+FIB(0c#E3Ol>3Vnt`}Z@yXIg?;H}iQ!mgFt4ie^1wY{)w3DzFG zo!UX@+o>HP;dx8#B=l{;I)S%VI}5+9q3sObM(rZ}wgKw`-d^o0^zBi)D|lzMo6vU# z>jr*A>8^5KIimDXyMy0Vda9gP?n2)ayocIL=zFNWA>lbleM{)OgS`cQPwAs_UbzRm zKHv|PzAERHhtT&0@2U0^`kpA=54?}sU+DXQ^#|{*4iJ8Os{U%=}j`}_%WKmQ{3H?a0QQ$+>(c(Bmp&bqWuA1z@ zQIIg7s$;})-iCGz_-J*k2a_S;c}pE9{JyV#013}k>W9Mbdte`ef1rLO^dF$NJ^~-7 zju-lIVB^6@sT0I;MnO9PJXxJ6{3e4<1piQ-B=jGm^d#^J>SUpx05%!?Ph|>b5@KI< z3eZQLD&`d*u&LmE)oDWC7p*uAe2O|<=%;{92hXR@5c5htXlH=?t24#C;tw_xe5(4f z&`(9_kHKfCvxI&I*evk=>TKb+KYC?0_;B?T4~~F@?2PIhahz$;&HNL0bi&t75arJy%c=0`i0Oh z2Kxehth!7bXDoVU8TjYwa^d%Lu;t(r)fGZN5xunne3`mZ=$C=51Ye}C5`Gs!y9#`% z`lax@6zogzur1v>4%0ZRjJSJQ>vcCd8t6m^%dOHpk|cy3d73%i|QyTMb{J;E*(Y!7&v`mL}_ z1N#=-R`&`!TiplAOWCFF7k0bA_Ji+MzY})5!M+3EqaF};d%zBW?^C}QcKg(Wknmim z9ujtY!482RR1XXNLG;#P@bA?lLjOJ35%B%$QQ>z#v`4|eQ;!M1-+>(iKcpTP`a>vv z9Q>$yLg7T*Rs=o;RS+HNge^k#2zdx$yA>nyLy&(La z2D<=$M!hKP&VXG6|5?2x?0yEj1b$wJ3PE4p4s+c9+3^1HY=?6n0m^Zh~J^Zwb3=V7I_;sJ{!l8|rOH zc%D=L5O%+U{Q>@)dPms(26hMhrg~S{-2}S}ep|gK>~5>~A>lbdeIV?92YUd1UwtU_ z_tifkVV+eV3H?2=N8o>`kA>eqpnVK}M|~pv-T`|8{y=>y^bb(_Dfnacnb1E5dj{T3 zeJ{k~GOX<5M`YxMgPdM{u$ExXWX({ez< zdnV0W=(B=BLRzn!!Y^zf;rU6+<@aw0ALc;Tz7AE}qf`x&HYT-g3sy$P~!9%qCLLUNsesF)SfH;mn zv<1KewSvNLAXq{0FfBsp!%#W`ynq%d^aa2o!6&Ox8t0YC=&mU66>7A`d1VFk(cqdE zBlH?tF$O$JDX}s)aSqD=T4F82l@>h{k#4E9i@WM{7leJ{qNqf*00`34LL( zV&GY{;=*qh^h$B?+*+&$^FYFTFRg?)P7Jgqz>8}oJs1lK&udyK;kOX9rNE17rG?+3 zV5PxJXk~=H1WK0yFQt_g`chzJ!SiV4#BuVVSIU70Y2`f_3<=M7TAVmeEVOap<+KVO zEDs6Kb6Q2=w3HypT7uA51WN!9)+&qR1fy3fgO}H; z2*2gQs(=^N5{14XdMgpUvgQ){%3v<=3YsDOR)E$3kJn7$Hy+FcucEnyz6wga!41t4 zdIQV?U#(WvIIpZ$t7%oi53AKR&MSwZuMS>Kt0DB&w3?9cJf_tW`l?{Hz>lc4HO?zX zU{@RbxLQZ!ymB1+I^fl{xF^ocFn-rfH&8Ygk5v6B=A;RTVdBqYX=F>Z(4g{ z*AlEfcx$bLuxkz00lbaYQP{Ns>j>UX>m=;jX`LbA`A+L1?An5L0q?AJ75dKTt*+pm zv~EJ*39K7yRT{`Itm!FcH%DqfH0$7^eD`Vom&(A?aE8+sJMs{y`>=Idr|)WOX; z%Hs8|vn_}7x|kboaFY#gtYPAns)g63)$mHL2426`!mH3ac+FW4uQvHot`S~^HW4pB z`GS)#G5G?MFD>~(k}o4W;bj(IF7g#2U;FVzAYbfpzVQVcU!w6v8DEC+ zMHo};C*y_GSiErJi!CO#=ZmUIc){c(uAlBnSns5#=Sv|bq<50Ue~#BPi}0#t36{h7 z`o&58z6vitzQT(RrgvY57Z)4wlEO)vz6Gxtm@M5%k)9%6F)$grEnXHliO=_ADdqr{ zSGb)2|9*061B**;EHzcdqEU4${M5waPHnODQxD5D4a6c&V=TBd70WR#uyoQ&EUYA9 zk)$1#KssQVq?1_s;POUywFed}dSU6}Ei6Lx6-y2Su+T6FkNiXMm_H1U?!)nTJ`xZA z@8U81eLQ-P#^dxDJVKAd1Nn#IA)61O`?J{q*c{_a$H_^OJzJy!aE8>RtI&MyH zh+EfNxXrvRZbI+khVnjc7a!u5@{zbr7iM}`{Sx&*6 zlsWO1!=%S4k&`4RLQZy^&^T#vVydcD!!%O^lTIy6A9ciJQ6EzTCxONY*iA(^Z-LO; zN(AO41krXP#CAlu?2KT@@W>FzFxOjqOY5Wc)%t1uwE@~dZICut8=?)>hG}nW!?h9G zNbMc%UF|*XeQlIBT1(c(Xk)c;+6UT)+DF=WZGtvYo1{(Frf5^OY1(vchBj0CSevEI z);`hZXrF3xwa>J9+I($+_PMrDTcj=4mS{`0FSKRaa&3jSQd^~csePrb*4AiiwRPHh zZG*N^+oXN1ZPvDETeWT4H`;b>hqhBo(NeWEEnVA%-5Gald$e!0z1lu)zxJJWK>J=h zs2$P{Ye%%B+A;08c0xO;{h*!FPHR7EXSB20PukDgFWNcnymmpms9n-7Yge?Z+BNOE z_N#V7`%Sy4-O_&7Zfk#NceK0OJ?*~sKzpeDsXfvjYfrSN+B5CB_Cothd#SzB6kXLd zUDvbdS@mqXm!4hEp?m8&^;~*xJ&&GO_tEp|zPg|8uLtOXdXOHhhv=btm>#a@*9+(c z^$0yukJ6*{7`>2QSTCX%)r;xH^;o@xUQ#cmm)6VZW%Y7;c|A_ApjXr@>G67kURkfA zC+aTU&`sT~TY6Q!nqFP6q1V)F>9zGbdR@JqUSDsZH`E*HjrAsaQ@xqqTyLSb)LZGT z^)`Bv-d1m?x7R!99raFnXT6KwRqv*E*L&za^C5#M`bvG3{-yqv zzFJ?SuhrM->-7!#Mtzh1wZ2*3qHoo=>EGzv^&R?7Jw;E|)AV$G7jm-f*7xY&>U;Hl z`hNX8{eb?xeo#N8AJ&iPNA+X+as7mTQvX3erJvS+)X(T=^`G>g^ zm;O?Jr8l;+A#=BizYfg&m)QoH*Ffeqka-PcUIUreK;|`&c@1P<1DV%A<~5Lc4P;&e znb*Mo+t+~N&Y4wF`pccNxM!e`)f&AXuB7daPoJ&s+;yiE^GCk z)F6FE5oB*pEM;wa)*yY;WyPLcv!XSyWA60#pTyexS0q~AopYt{DOJ)A!0uS3u9Z%I z(m2{)SiOoh_fV5~_KY)StupN!rDy*! zx4orJyw$KrgY~t5|O91{Pbwl_mX~7=@19G&as{V)z}-WB(WbaK z^JLs3s5%}F`$8kfr?n966@jid>NdH?VpqyC+-X~aQCUb^~M7-8%hlf~$@%QC+` zF($3g>KJojplK41bxJU+=JzxAjjm`aUb+9vzf?`$f@V=7tg)Jcmq#3K8ve3S_3X2p z1;0Jb2A)m)UKMZdJ-TkKc&$IDYt~CIG(?%?%8McL(!`<7hY%{pZDRMtOV{ z5|O_@%>~pU5&06$8|V$p4&*B}Vs;?UBMdck&euoOGcWuGLLUTw#C-TZOkf!5$a*=y zX!V0LX&Lp=v~@0QWl|Nd8ijjgSsAOE$`iEc*={>MLr zAe^~^R&f5w@1*`eLs|Zx-~X#q*njNXR!&j=wr`uFByZl`zkvPemV(wn*YVU68^i3R z07s_2ba(74g)&8#h1sjS7PP$c7-?&M2($N^1%%8pzfLS5Y=`)U*s0|Ti1Mc&2iw)( zb;@LK8)84`o8Nl6a(G&^9O3p0&5>!lx_W-=)2<(-9jOy;=NTO)Wa_(b#U5fPziwZM zJ*QT<(2uPfYPY@^Dvotyd7%B(v2by$AKnkNd;8=U^@;NfvX4iF3){w9g6zi;j?a_t z2HO>{g$Ud8qx|i`T9~jcS=Zl=t`sJ0XXXj8(+7tN+s#)3>;<2Ph~sX)5oj-%8!T*B zZ1A;v{TwW8e;w#+zo&-?+tD5T?C}1`&y6~_>Ev%Khk}G{=z;+I{j5R4c2>iD_PPFn z!Zug7eD*sNks}}N+1}sRu3b1#*zQR1vzP7&5VlVi``bq<1_;|neSGYgV}8PRd43YBsv-8UFM~zhWMA`GgRh3z^H(}$w%iV~=g)HbMfNGn{J1;V9+?&> z>X6nX)ZYEnkta6|vG8VP+Q;O$cf`aZ2xBg;yDVbzbGU7lx03H8DuxB??fAex%PQy94dcyM0C7%C-r}xCZ(y3=}fkdFtFC`_e*R;q(0U zAp4h@zM>9WMh1y89PjzRcjU>g<1X1plw}{?%xxI5-zm#}XBpW?)XTm;yK=Z8uO-SX z&ueLX*N+T&Em5YPW1nT@^+Ub9e&}EJ5oOs&JeKTh%Cb*|AJ4TxKl0k3US1E>%j+P*2y(^kf3+RDD8t&HKceN&vLt&GpKmHkCq8N+G&rZ`Vq8J}q@ z`;@jahST;8 zGUkgBBl3vx&U%_#bBZ#YkCx8QnK2*z@G_@}A*^%3wRtk)%)Hxqgr4p2&Re-MVp_vL zatWVox6O5O2%nrE7f0q0KH0X)wj9Ei?H}I8TiCMw7ZuDYY}sF5MR^HZ&dX14XA`#U z)ApOZgf06$d3<(Z%YILul0(??IvLzNtFYz$z}Nm+g)Oh8dRjJN%j>OB7B6AT>+QOk zUD)#a?08=nw!CjS^QA6qdCeF5CX2A;xcKN?R$6-9Q`NIKD8Ojyr>%u?BY($jfUyRcgPMuk1(8Vm`xRkf`cl3-2{-yE? zJ+CG9i;Vv}yI|iE%-xJXQG;`fGQ3vVM{?dda4xr~8)H$GkMfGTaV~Jr$}9AYd+iH5 z*AM5N=WeIp8SA31=M+9=-_a*yqcI_uI4cIPo4Hq1roU@)6b9~Bvp-;|z-}Z2>XZFiqFT6zC zQckGq9ESbS0&v}P^$5{I%YeJb>xexzRZBYn#Dq#xP-)UyrQp47|srytpO^dtL@dX`~-QBRp;ih3FA zsF!1kJ{g}GGg(H)J(iJUie(u888ca*H^pA+-xP1@Q^rI3lvdBG9J=a z_62Qa%%ts`;wo)rJfy8`f7;5JN!vHYRocpUNL$%Qw3RWFwr`57w3V@rwz6MnD`O_h zuwNMWsHe<0NuO_ux74%DKV$4c%!92l$c*@$?_hLB{9ku1IwStS zH#RyW{+InBA|pO;ofTo{@x7m*qBLcsHa8L`^u zkAfMo`ax1;#vIYY9hosNl$sx%F(1jkqfg(bQ5o~m(K|62^Pub_mRTDblQ9qOn;exf zA3dEEoiPt?>Kc_XAJuLdoiPu7nYD1n{44v8K9A~!GUnfgr%=W-@5?@7nfvPtiMfOQ z^{7Qm#{664W=zIhUnwvqWBy&dBqn3tZ_%nq#{Guu7y6tMT3F0e?4xEM6v^n*AhWQL zd926P3uWA6taucYagTAdYavmF&kH^M9C;3V97Mp2%x} zdfD&P%YLUH+2)jGo71Q4JIb<;gda~|(~sSg~@FZ-81W!qAgZA+iBk0{H&pdZ<% z^dtL}dfAWE%YLL!*`D+x+n;*bp47|srytpO^dtL@df8vp%P~d0jCIt@_M}f4Gg(H) zJ(iJe%Q7-%vOaH$z0|)c-qNRxhx94inLcIAr0tvHDs5#vq^;}=+RB(o+c(8k+RAuH zTiO1!l`)gHZ;Gq5mGO|avX5vhVGMtTmijlv z*n^(n>$@nkVCwVS%eUf#S>xZnfTt8$x$aPD$gIu>{yUF!Z z&PQ^+lx-{5OWFQ%y_D@Q*Gt)7a-Ej*pj@YApUQPw_PboCWxvaHT3#n|eVFsFTp#AO zB-e*|y~*`qUT<=JnAfLV$L9TjT*v0ME!VMm{mXS}UjK4kn%6wn&l%HrzaiJmId-^a z&c5Kiid-|H%(ZvMQp#M1d5T!dG01gt&Ua!AVr`symXT}bl;t`($GBV%l>H*(vs@2k zJdx{=vXA7vAlD%oTjbgxW3^lxWE_)glAIUhIwWJ4T>q4PN1u#!a{ZHWQm(toK4KZh zSh?=Xm?_sk8I$F@E9WD*{>gbzuDf#nmFvB-@92{;T(0*r{>$}e*+(qHxkIi$b1snU zy_{d<+A`-~x!%ipU#>HAuHkz4>-|EXoTs=3F8hdUo7A(9xE{`a=e)+XZeAO_$Kd+5 zydLDbH_LOadosqd>e+^DPwHj+(~s;s`jKObdX|xE(Uj%-vy62tFUJ&p%9u%88TVL5jwzOrF_ZOS zto~>0rT$IvmOf=Xq)*w-^eJN|ZQm4EX)EI)ZDn83R>n-)zA3KKR>nix%J!$NjG453 zQ(UF3jEA(9eMDOsGim##xJp|Y>u4+cg|;$gvJCr$agTb+jFa^Frg%#|%ltFOX71Zo zgZtuS?%S5RZyWZfb0haU|I6IB?J)LhV>=L;zdm!{wyua1$;{24xo;cxOUc}~Epy*C z?thfIZ(HWRZJGPFW$xRSxo=zMzHOQNwq@?y=IlGheI_&aZR7nB+krSYZ|1&jnftam zs|)<(lcYe;uVErH_iYouTFKnEtrXfJbKka_Vt=;GecPA~KGriko^cF5efEpy+t zk8vGj?%S5RZ(HWRZJGPFW$xRSxo=x`)%sL_bFoE?eA|bg1jtujBP}QSj!+UE*2u>rI44-FJQm-d#JS> za=@VgQC`YN7U#(*pZrIT3_p~mZPn=PmOPdy@AJeZkF^|fMjaq$92c@Y?n21&xKgH{ zMV|6J)-uTSDUR#0<#DBdmS5`8^IW7Y&sX|nJ4pGT{-rH_x~(%<&Inbm?f%0syM>r_ z)2|v!^4BxJ%$npPK7TdZ2s}K%{NY9p7r#FN99QYE@ddsgH?f&f59P-K|3bMYpN?_S z&jftmc5!Z#_R|(-H)*%L<6yy69Sm%FirV%x14eEIj?*ffRv zv3X&?F6ja#uAUNZfhNSM1Bi z-OTa{eNG_DDe%W~+cQQO(n{GIYjuY|M?kMh_dcj{PI6Dy_Z>HfC6YkBL(_6^dKFXpv3 z#MZUS&Z?Id^JRej<041D?zE5Ht&gK`x+u_|e8KT^bXPw6`O3P&&(MiMc2szrIPTN& zzV?Sd*R>X-o@;~s?8izyQHO<%gY7kq;;bt{4^wt^^Rrj_*Rxun&P^``+XaTjS-Z>U zc1iu#-PcmOl?<_uOo+24f49da{UmkpcS-&Ds31cg>va45t|lHoBaXWaS+|n6gAIAy z-9?%j@>~imFb!GfROmyDT@!unrO(S* zK_6IYdQCq&sYH1z_RNs9$U6RZuL0$)bA`vJrHu@*6Vn|1vYSEn{f=>>{GQvv_N%3F zR(tr#`c;U%?PQ!)9sXbC3bmWxjKtgVG0F)2cT2Fd*vvAbeD@Q< zc9)A~gr9MzLhOOjWks9pn;B{sZC+NiN%}+Vw$rbyXcH;3O=KCiiL_;#NT08_17&$! zwu!6{+d7yE5_Qs9U=D764<8|*W1}_q4sEZ zS&L)pj7R1;l4Uq{r0whH#jzv(^Lp6lIWGHM){SF9)|uD8JQrT`A3gN5Ypg3Ru6ZdR zU!jHsxt_#I2#>SUNgY1%xVugOhaO}eK zD%P5S^0C>&?4dCwtUd5!G!L_5;!9W$7U?c2FMXNjl4WM4`Ww>rgT7S^8CPFFmm|9y z88QxYyvufeU4H;&WPL`$&o7=>JsSRHod@H%@?0jNKJpx2uiNW&c)f0~*ZE1c0jbrH zHTr>ftkugqIB{6(NIPrZSnKgdKi3y?L$T{eDJw1a-qhFyp>|rXQdZI}JGJDgP`lK- zXp=`Ft`)~a?U->TtaEq!rzTC0u&>N3{x|t}%aFt=QzPt03yWL7e$g}aq84dqIal;= z^72U)6MfZ4JLpnTYvAGp*Tby&?IHOJS)&6trM{aZzdb5PA#31wYg3oJm)~x9Kgybu zSk)CeGQVB@S(LRex<~5jo00Z>_JPg6(zl3tBwK$sV6P zclx;nKRjP~T%ND256@TDndd8y#q*W*=lROI@qFd6UT<^CvOcf3+v`4G?~B*#_Ikg( zUjNto<@Gw>Su)pH(KO8V%M)W!o_{CUelQ@~%9mK`Z?Y^y{a@=t>Vu?aC>Fp zNUP+I1xCQd5PR*Bfb+E>+k2t*ioy9U$^$Nh+xvV9Se<6uMtYer zd*k*{A%8zBzkR$_n6T|OEZi=6GC;`v&lIp53<|W~o!-;9J2AgKZbd#JKl{C)y|lWo z<^8!~B!m{Q?~lkUH`KXvn^2G;tt?1y~pj+-2L?WMf-j$!}1 zeo6g&c0aq8C||RSkNw>TwXBp5Gg2E*#tyKHN(=eX;z0XP+|N|)9pOrv6=Y9IcH~#9 z0`2S7{;vOLJ!Re8R&aJcAB-)X&HQlO2d+aqeloJ`80VU|)Yl~bSZAdXRCJ)3`rZ`d zmxO`lLZ21Js@++PK`U%y*~h;g1q#)Q$mW z{fh-n8-7dQPcT1CA7E~JT*CYk$6flOra5=R0JC?6%I4hEEJpiZo0+Gk4KS+>u4RTz z$ZBNyw3At^`v7yvaa0$vaP{P#+PvJ7x2boJLrl>8=7J2bi-1cPD-gUZL1SS6S4n?2muCs>08V zETxPw&j*+vJ}qhV!*OPp>T0wvKG5{8)WrxvJ<5DC*_ea+RWqg_>BKB{;0sE*$H3jb9 z*G%ZAZti4OLAhS-`{^BMyz2jnYbeU?Z2Qbr z33gAfmNDwX|KxGqjqC8wdQR*8hl};>b>fbT^(^9l-^F@PX!y{@>*GuKV||CL{!?%w z>pc}k$9j*4opN_?3hPT841cWmKp^Y;A--pQ z3!yyg+Y|Pz?-cmsb=Wv$r;FELKP|<@>+by$sV-h`_JdRxuQMXAuhG!+dK!dsypEc| zj@Qrbd^=sdZW7%oE?zH7lG9wgPTn_d7u%8b;dS!mradlR7q24sy4ap8z+QY zLfEn2o=(4L&&n;5MSFf0GsYE+_AD@Htc&f(amMy42wny4#qTemy%JEK?L}mJ z&4Zrp6@hYWFCF8N{mgo>e&gVW*XLj0Y(Mgk(0*fq*U^3&%BP_H^27ca+HWrWvHj?O z#JKKh#9jWMq|)!)w$D;|oU2#Mr17|Oc1%l~bRyg=bGS>|ldAd6V#n(k4J)@XN9|i= zbnMvLtT+5~ z>9f*^zg%jbc525iV?|rHadKQo!~eYynv0?wzw6;t3 z8b!ksOrpBCmU;ZMea4oh@usNPy_RY8KXHFulm1@Z>XgRsH`lLavc3ar?=tp=B$)m` zoHb5mNifZ}+046-F6wusmP@1GPUl~` zSpGz6IdPn+2|Wz@W4&5Enrg6~!w*e0SieZ0X$I>@Wc~O(>(^`PRD<;!fpV;0Z*bPH z*@USE>(>;>`ZWf!UM*qAda^w07Xg2)ACdLr_pG1rgZlCKtRIiV`Za-{rGsjU^C!~( zjKt>`8$HW&h-tXPbOpY&t8%LGWA(PKtz9Bb zBK@rybk0S8#G0!%xE{TFWz=4^A(hzo&$+J8_tiHC{y8_5cy{Qwu7QJknP-N6n<_9* zUgM+NgUmB|@}?1=f7ZsNpT{?Qo3y{Uv8Bm+Q_uR=O>AefT=zjeP1-lT)Yqh6B`hhW zL12>kEHo*RNPb{YN5P3jOFvDE+$PW<_U)yc60;pn;`at|MZ-MiCy7~ITe@5^W&`Ig z=xF{PF*<=F=kSC;c(#n~6g6=!F~*iWno2VtGqSpg2h+W;>m4C}w)!2FO3 z01GM+N+d7}QZz6IQXybrNJW4}Ar%8=fm9rr6`!#{N+s~gx|PH~u4pr}Uv5aHfq5a7 z0s2BJ3-pIn4j2fjJTMqi955781z=qR)ACw zSP@cvU?oTmfC-Qq0xK(xl*YhBNKJq)NKJtTq-H=9QgdJ|q!vI6wQC8i3aJ&a8l=|1 z8j#umnHMk#SPK%dHY8@qs{^SWur8$bzE%Esluoa{}z}ArZ0^2}Rfk}{2Ywi=(q11AwgOKuE0rC`iNs(DFP6;FEl$GFtc_0p#)C0VV^JaWo$H zJs|n}kVXODf<$}|+A#vh2+lVz#liW`hHr*)V8oK;#5m=c;2WZxK)xB`c;y=*j#Gb( zPma?-j8u-(V2n?`xe3Ml8jjO&jL%SD0gO+M(|C;30>EgD&q!buj8Bf!Lcl7(A{eI} zuf;G9Iq8oJC4^%s2#^^0&2(cS{b!#1WZJ&Dg#}p700Q8T5+73sFeYXMGZMl zE!2wRv?^+40jr@_9H%u!Vin zfDKS9j?;#yRRdrb)R5z}32Mc0+7z|oIBkYnahx_stvF6wpjI5GZq$(Dv?U~thgPT^ z$7^fUj^nirYRB=KgxYbuwneRyfbCE#j??z26~}1@)QaP@BWl$F*a@}bIPHvDah!HV ztvUmTp@tl%!%-`a)9%3Gz#ga_$7@g2j^ni#YS$Ck8@1zjZHX(B*L5p=^15%0PZih( zpZ$PI_*4YqSryn8pB$&{@X2x79-kbi9q`F<+7X`|r=9T0aoQQ59H(9J>5S76V!U+6 zC&x(-d~$sD#3#pRFMM)*_Qog2*;r+q@&WKee0~J{NExq808UgUDU*Ryl&Q)z;B z0M0Yxh0jGET&ye+e2KCYxK#N%qTPHVNDe+ycG@ zxD~h!pWle@zk$XJ|Gp9W?FxS!=!KsLZWow}&mG`tN~*wg;4bi8KpXfCKDUYQw?X5D zf7^t98EWNxzf9mF)Z01UB7yVJ4qv1EdbAdCK9KcY2Z{JOa2;?XB;o?#M&JfW#Dzef z?1MUUxheZ4ixF2`` z67hTB0pLMM#Fgm36yQ!s#4mwt$5cqf)j+mSIway6U^;LYB;r~i+tr3dJfs{}jsTBB za`1?94E#7W4jxlZfS-hh_yayq0Z&76@Raf+_!(#%{82dz{u49~o>hJZ{{p8V7$>egnS=jf218 z40)b61)f7Kc$`c4WPQ#;BA$YG*8eO%d7Mj-h=<{Y?Q#sC^!F!5=Hn1U>~m2Y;r#5cnMU7x)Y1 zrSb~+3V0v*5*~?21*pc#cpgF z+4zk9@BgR4=fCsczq^kA&brZ8mVemd0*>w_1>SO@x#e~SAhZrSixre*n8 zruQNHSh?UNR{7QaYwCP6$Sz(t)*9IEJ6ALO1>M>ru~xvGEvd~t@&M?^uM4(EE{?W- zL;0iSL+r=9qpd)ck@`i0$ED0E9B2Ev{ipdm()>h0_}x) zVy&epFZB(f-_#)3?lV5xN?E6*)%hjJ-a0r|$X;WD?N*=1TB*>#cPzvndn?*vowwu< zv9IiP^p%IMc^GES;yVv*_D^&TwCH-_j8L6L$_L0X8 zMj7d+nb$Lytmg!jk;lEC&)bma)f8o9J?}s-&;9i>_n?>OSOeu{fBgZyY?ErxAMy0d zvsdF>vYjcjZQnxMvJIu5*L}LJPPmX_mDx4V8Y}y>GP7?}j9V>RnPZow3oOuEG5U3h zGR_Y_YdkC*Weh)FYOUf(fA@U8JH zjF*d}jQ7+BMzMEVnHRcdGf%wV%B&Xuz}S&J%J8l3XI6W=m05LkHnUSulu;uz!tB-u7pa|P``-puf=jI-kF4b-r*niI%~ofU{b9!G583UDsGn{nQR z_yG9}0AJvL{S8fiB?5Mlo|WGiNQJ;vto#-R7J;M#DHX+M433`*7>OE033OJVLvZ9E za6ha#6LX-xMSw+ss?dj_j;wEftTod-s zfj0%11-0Y(7ej5Mfq8(%fPq*!4h8ZJ5HTxSkM+)qPo75s=>35aXf3X=7ls}E_<*tf z{P7J}#EZg??VlYIk#CLy#rM9Vy>wWzAG}dZ_7~q!v0PT1FZ+uzoBhQ%X)Nc1^W<^( z#)|#*UtOt>@T}Aq7w^=#LLVv4n{U$CU;J$c`>QBgj_1ib@x04eYfzK__{{~^uQ{{u zw;%j<3fq|P6rA6lu#T)B-y-l00^c6+%>myU@QnfA7C7(p_@3b3{UqMls))|T|1!nG+XUqFzf{8)R+ zyhQ7Vm-mcECra3LU&dMA#)O&e^TpW3YL~anGiA(?y+iEXr}4M$_=o@^i&*QNN|BSUY=M5zk0iYehF8W^lxGmI&#z4IJb%6cQ~8*Eaz5Ncg&U%?%l3Axhk9c$5b%e z@A5Xohg2{h)qG?u2#GiIT*_sBrN*0|#oje${8Yuxjnf??i2yvL}2XKWgA(2R#>$5t> zTo`DY#A9fms`>rQeWNRyidXLc_TL(e#XV;UPjul7q9e*M5;{?azaHZ};zS!pPG@Gz z0S%*&e8!(w-5CGe{m06&OkLiHMp7NRQ4NPv=)>5zzwg^cvxKt@?cMq+j#&m#;qBxVCL z^5=zL&KyCIi2Pd$&UBpboEbY5K3Pv^_r_32{GRh3zvo|Aa7N|4O8YWaH`H@*_ih<& z{qK+DjjgSsAOE#TET3Z}a_-|lENO{<|J2Lx|B15vKlT6WjG(wzdMir5&O?TlMI@XL z*~hwzgB!}(qmxp89rw(AV#<8$Z2!e6<7zx}kDoQ)YEyenO5PVw-2>-CUv6#6j&)Dn z`@eub=!=w~?oZu2S400`T1rykr|u6o&9^oscTZXQezi!=G^5PSBxgFF; zrHq6eLHSN#%8u`zxrcuU*{5wv{*BMv&&JQU;&11wq7-`S{tb`>dESR&h%CTzC+<%~+@%c(snL75FJ7(&9D=N0(4s*jZ zcc}@e!`?+Z`{7s@hKHH-gbu`oMm;p_03NG_-*(0{j;n# zQ^ut<8hYEk>{p9Z>O|ajfB6&ii#MdSN&Vg3_!{J2Hl~zo@wj&CHRrwy z?uC$Vr&LPKxB9$0q9XK(F{z0Q&$-t_Uh7s;f7$+v+f@lZmo7|c;QxzzC**G1H}2e> z`m?)0{1mI<*?`0?X+OK$L0*}*GcixlFYW>VkWY@v?JAP?i@Q1WvsRaOE&t-2+Xr&9 zT$amc<9YW=_-yuv>GD2w!5stHuU`q*g)Lqt}$VIZOPdwV?vU?)x zyrAlXDs8r3b{GDK?6#g@uo#M(Z=A77OEPbNJkA*KMUq+S`Z(iu=eB0N@_}(Uww-zH zr*THx+wIJDZ;vxZ&T4Ot`)RB(F?RS`->8FGc-t6bO0zcRtbSt+ zzv*qvb~8sCTNbuAPkc7Ya6fNn{uMgPxKO#Bc?kH`z_#Y4nWK!M8D!Ntt_O}A&#ufeiY1&ja&Mn+ zoFDSD(Ja?;BQD^o(dzRJ#)-!FjdDZNjV_O~n3Fc2Fc$t4WUk$K)99m@Gh;r`%$6G) znTNLKGOgM+04?#45Q(*d&a!A4UMUVt{D3s zw=?Rl|H&wOy07p_AAiRMQuy^PjjE^|_RUSomVFh_slZ(JN^ znjP8(8RgcyO;?*B!r+wII*Xw06N;UB~?1OKa`3-j(aQ+a&($&!7W* z$5od9BCO%KbL=7_J!e`zV>&Z0=WNb+Tv6pS4d*k?Xq@LbQzNtj`GmtcmSu_j^*U$P zocQFkj`J)*^bs>KXKt3`j4kJ8J}c$K_w?)I(eloK&uIR@0HA{7apnsIQqTE-Ghz^s zdOl(DubYB_)bqZBJOmOk3y?ExC?q2Px{A++VUUQtGvSOI4vEP7k#JysNJMJ*OjrQO zXHY(m@)?%@sLu&a1TX?f9e=GK35*0%&u4W$D@OsT&jU>~Fd9fb?-0miAQAbuTrt2x zkchlvCQOz??+1Th==KgFO;^+ zmg{A$c=Tub{?7{A*}QsLE!=;m|8=^wJ^fHm%V+vYDmrsb{-b zUtN8c?mw=ueQ9kM>-zs(TRXu7>$20=uEWF#n(4InuPOa;stbHsoFMF9hkqM~FF z5K$j<&N-kciZV zz02#1vTd%1YD=349&h+fIX=ln^{HV5kMnx3yprIevNDR`Tlc(G8drO$E(Jw!*7K~g z&NvsE1iOjJQsseeE~*Q4B6yv|rOGzlyj3!{2u>_=mG3*asD`~C z&SRG3DvwO}R-Lqs;PZWZDE+g&RopIuH>}@7dC=cQHUHgkp5LLnvU#wJYSxbkJ|eEW zGHZ^vs(Z5$ywm!wNDbC!Cm{`McmbG~#@=5%sVof$rw$F}XNlrQ&ImFJG; z)6R8KI(2nXjeawlFLZ?US9+@=8bxy72kn&weOy$hwa4;f@(#-Wo4i$e-eY-_A?=jI z`n#x{caG(jh3%Aj{)MyRs$R&Uj!`Q!M4vrIX4IBe(9INrW7S1tnkGn4WB;{c-6ALXK2SUH~CtS8EW z+q_kopT_h3`EsS^XcyH3ooGJzlU$hx_6NSvymC*7a%!ZDYU{{o?(sH6*%<64^P~C9 zr>&IwQ7)>p7BM`bQIJv}j>n-*47c4Cs0@JX*eo)JZ@3(&T)NR)wQ6Au*ZAzOJU!k; z_3B~_w`v%mwAtXT%F>MG=ePJNJFSN66&}m)EcH`1iE&X)^@!y@A->Adu`a6SnX%lY zwXd=o+Rf}hEO*=FqfB20=YAuW8*lSbeuj0v?TY2?z7pjkxJI{6#`5*IiE{cR7uA5( zu{^;qQ`uvhw`%dQSU&4mhO&8@iz>oDmRFqLrF1UxQf;b<;r>^4DN`1~H7$+dYd;=S zo|k*6)^3mCMqQ38zprso?M;f|_otm#I+u8=ex%0mu=W>}tv0);3DSLwRp=SbyPd687HxG=?b43sr7xc;3w=FR4Q-4?|^zkRJt|K_1O^ldC(-S4e( z_*xg$g0o}!&XQYnLAj@@@6Kr6LF*13(!y2c4f~y$dWH6KbyXef9K&O#U8cY4d#QS` z7#`o@41M6>s#^ClhCd#0npOmOsg731@R!9$Xn#vr)tmOQ{CVRe^wBym)jA%_>&bS| zAqK9hq8YK=VcmAxM8jLvEk2gd+_IQ5nX78p;aKiFdok@C>8*MJ|g%swYol zdDVj1^x9_^)uIQnd{?u6^jw6uO26X-4zgc*`Z2Uazy$v5UQ>E&w6|*6!U_Dtk7m^J zx{E4u`ULJ+*n)l=>8#ch6t&`_}VvWen)28@ntGvn z3Z+)pTvX!8Xs%eu=(136)%S#vynSeE+VqNxYQ&h4{F{o?;MU%%j9nx6@botH(q$J_ z-(@5C&|jf+0N9_OkKos?x1|YScT`633zdVY^DP%ut}24ZIuD>OoxD|N&PMS4H&)T3 zVcx18dn5S$Q>*FGuP&1%YTt(Qu5B!WMPyFpjmxvEB;jNpz{Z|ECG=s#OV za80N8bd-my%In|=UOndxz2xGl@?JKIdmej7?|8bZx@{fBwZFZg4ZU1d?oN?h`Scat z-^x?f$vl#`p7@HIgT3SZF+6$R3wpG-r)uMcG5nKwPKUvFi`PZ*c>QNIJ=#-s`|l{e ztLy;{a(7jI%N@^8jJi*s?DtevCXI)o%0L)J$jWP8qQa=hdNp>i$SaNsktX~S!x@#rZrbJC&?OKy{I3tp2W zqi&NUjkM^Eoi|AN+}C9KoEs$5@-+z@d!6X-(W0OJzD~w_yda&EE|RK+IyC07^wJU?tgjqjWy=|irQY}Fa^US&dkHk>0p zCtfFs({2;3Qzmrkw7X<_{B`p3QZ>1G$%Lldc|>|%xJKm0KS+FXF!D8tFe( zlbTI8rR@{zP?PLyWN%$9Iy>8xPSe+>gY>SEQ}qq#zT2iWXiis$n9;sT7IfB-tKi_Yqz${8(RyzzscpbjvZ;#|jTmW0JK9=P4{?PA z&TB-K8_cLh@5Xd+_7!3|)s8MLHlw8hP3RqRg{-RYKv}gJwLIoPJAE^wTW>nib+Rj@ zzM~Vpdh{~60yN{J-qEQSzp(c-i6~Da^9H++_*}XG;yXa!wl)G z9yV07;sIH9)S3={s!PAlGo#&{o{{EoPQ~AK==k}@RR3%Z>3q+Kh99g;TQoGHehx3m z&7lqG_r7&$$X)|_@6=1u4TgpCM)}^aIy&`Q>v}uN= zHoenRix%0vCbm^|=*%lxbo_f6b?p0^bc~l#mQr3kE=8YCdHgD*PO?7zvE_5f*BnE- zw)geoMsp15`*@jr@GB#_R^xebk9S72?TiL;_qC?f^Sz#;XdQ&8Uv4D7tzkjuIXNnx ze=w&Ve0}80CRtJOw6&s3XDgbN)`gUTHJfFd}uthYsVFeE!P^+sqGfXha@(lpU*5-6fH5K zPTS_ngBqLA*VPLY;VZOh{;VANTyt%D;_h5UFaD7HY&~1vc4#$;A30Z1oK-=l)MUyF zpFpT%VXh(-LX9(gGUa~v1Ud13j-r2?BGPtJhTQtfA(GuEPQJvogfy&^tVm5RA^8si z6)hH?Ce_1Q$RFmPCM@83@wx-oN&KM?A>Sw7AZz!nEY>-2pICX{2)Q}^0lC|2RdKt6 zHRNfB8zJ?3z92DYZ-z{*eoyWutScT6^`3Z{-3clE`h(of*;0IF;}0UAe?O#pTOF$5 zw!8S|ygIZ%^(?POFBB;Zi^ zA6Ebl7^sKH@zok%u+a&CWwjF(9Runic6CBLOoxX93xAy6YB#0&#TkbV)GkZ)i!u(G ztKFCQqHZD$7?{8-o_at*9S&s%FTm;n1Pf_^K<(mGzxd*SfZE-ue$mAN0JZB={bGwR z{AxF-`b8FB@c)E4;)^T3(Eo9!K9XdVOI;7@de&Rdf`OZI=-Mc1L6z1 zTG*@(XTle8wa8f=c8D+FYQeKQ>=0kL)#7J$xS==H)k0`>xFNn^t3}c3a6^2dRtu!n z;fDAE?GG=?NVQ}dseVz$U@Lqv4uWtdWC)M|94v*{( zVNGFB4+Clddr0rE7+&Tt4Lsly2L^&*DF@1d*iINMhVAPH#CG+DVGI16!V=rHC6tgH z*cOQA!GZ08*bkb39f?c)9IqZ;UtEGG4X%C@>H53;CIvh8m+o)|J%Bxc=-NW(J>Co@%wtP)CZzS0qfRKLiU8Pu-+1TOW|r5#@QF@ePG!Sh~b<4CH9xX)G&N=Ak+uI zau5*1HwQ}`EQP6I_~sC(4~9T6{65}9*4#ViwVQO&@JU0dEQ-JY63@Sxp@Y;0XwBKQ#7`&DM^%)SZhQ#2t zSrTVKK&TG<&C*Dcs1Ex~hFS{Llcm5m9S9M_fHb5!EDgildONjb(G*Lh)r9gf$@OqC_r`0-&TkS+YV7+7}uwc z-rEb2YXuOkb^s#M4nmaNe-s@O#;rjh3)km=)So7tZ|y&50(dO{&uWAJY1jNed%hZM zg%kJ%#t)x93We5lp<={6^)w)?uZ?X}G5O#kdSqrM&urr+{(^Gcr*uB$Kuf8-Vrd$u zD_t3`le{LA4-9X~aGiNa(s!%7FUdhu)2ZM`J~Oh!g*3>)_+?(pX}r# za{St}=V{Y<@e@BWb;L5-_SJMg{-L!nKlFx;U-1`jceJ6{aqtcMHgq~4(bZWfg3H+F zu~T@{mkuJjMHzcKe+Hk?%tBg!K=Dl8xnUF0)v1hq@k!(dN1KVZupO($N&IGu#$u37 z8MEif+}Gbk6o7r{*kr!?q>bofS;ngVPT}{f4aJS3H>}%%6#k-9Loo>KvgfJ1WgR^+ z{Ky;DSeC};TA7J=R%Pr`XgdEgNn4y!ykS|%>D<@GP_(u$W9DZw_>+k5%1G54mT@FQi$1w*F|=*HwQfSuKbPfQo5|-{&eOj?86ULQ=Wn zQWMeVLjgPVJ(*wX>nL0YuV4@Rrtv9L8jIjZ1}%v-ss|A2HVK5Y0b0i%-&QD%N?8X4dj_o`1Nxa6dVkjkul4 zC;V(Cz9fXO7gy7GR7*DzZ6RlDMJj*0-A_wlJgBVq1^7zk=BJp`3 zThKd`hy86YLS_YD>8ZETn90+m6Db^X7STUZKZXB*JSe{@6E))fTdJX zXAak~Fc3vw-&4ccbNJwFeew7&6Q&V8hxdPIBW%vRr#t#)^ZpZTg#B1kmh73$CmDH) zRwqZX;vKVinU0rOQNJ}4yJzuResHamhO@_`GPwCxSE1S;#H?SX^US&aV$&oqW;8FJ zYZmy3B@I1T{maw2tgX9fG-ovP@=NC7ZCi>J_k!8^amjq}9)Gc7_-ERFa6E5*$ycm` z^5}x;d~%Ja^t{T3{+2M^Q)&lRoy=25dr0;l<*7VAv880s{gBG%H1(9uH~c{gPe}9- z&!OEO%2K(uhL5z(NBd{-F%=$S(9(}Itt6QTRW}h%V~b8Dv- zqQP({rnM%GANuMhdNg-o1E!>M%|1<-^QHc5ir#E)^2D0)vmM!);w)a&*q(Kq(vg+@ z%H*uQ8B^TyV&@}s__G9CX0Y0e%~+bnw@>nAx)-}K$5ZiKw#u75?LUOg89tp4sOQ6e zbm_uuCr{@qF6*;Dec{tOQXIe)S`Kh#ql09PWbpA2QfgKMhXC_Vwd~}($WZ!=CFCKBriIv-yGi!~h+)nwO zrpK*dW<7KG464tt{PkTHzj)yneXx8EE6va01;&Ofy2l)*6_(9odw-xP7&E(z+YiXVS#+m%V zAzQZTT@L#)B$F?@V8J4X6tFKL8N9;9PFiQ*(R5zf(w^y@&tWI)XYg$fHf+W$c&61r zTfK6S)-jlp#+$e}vse3a7%ffX3okZi+Up9KVr43y5$7zeGp1=OpSRDIty!AG7LH8i z!p@$Js4QSUZBqF9S*~pClod==C-Ffmn=$2IIc$(=GT-*jfpyg1&y0H{@d=5|rFHD8 zXL7|xH|EzfhaGUA#Zw+Qv!m_yv)$DR{JEC9w9X8;-(UCfU_D%Nm{(3b?-bsYrLNu2 zE-#wO+xa?5zfb&s&gL;sU8MeYYg;C-YUjX)TkoUkrdiy{v4v#c-7JIG+v>xn!f%+K zR!KZ9)>kSo&zr^DHSuLSm-bM-xLLfayN|T~Gp8heV~syc-r>bMz%wdlyAL~{@5R>U zPUjIj{n@&OjhIM?=c!)4tXV&6RB6Ny{f;C%1%w=t#A7?{5?|_{?A=q?8kIr z`va?|^C7?d82&zK2ft6M%$=C?qoJ&-G=m2=cV=CicV>fjWN_;|54O&HG@Cj)kspC} zIG8t@*$qzS^B%afO-DMjxIW4J9`}^W$2ybxh}G9h*t}9c-M=J3MU8%ukGF28vX8&bN^4 z-m_Bqp@z0nJHIZ;=5MuZr0w`!&gR478Z*mGQ`UW47T?;>L3*BSdXdRj%=2dFk6SUr zr!#r}Qh3JrTCu1161jJkBeM-QXAQ1q^6?j3*=gg3Y~jQV?lPAMd*B=f4e-fZ{`Pu8SpCXd(jVtf00ut6n>eEUWh7GB~6{Yi>V>|^BeWsDkC(Xn~#~SP2=1h=_YGUdjBhk6zA#E1IGu` zud$l+&h)`lRRQDdbDU{uTT6PnwKKKr(2(Bm??_vIG^X8c8dAeDYkIevDIGo2f?g`I zpmCMp7jUwt8eMLX4lC_x%KY0Twy+7+`F@vVuhyqd%`cEGEsbb%pR=TABV)Sq+a+S! z(t$P@c%Kwj?1X$l z7`G}QU1ML6D7z}sVEZd_e$zg(QsX_T&OS*xb^A;+1C7TtQcoFv|;Pye#nMdqf}qkR`1CnMdBsO^Mpq;Y~ijqai% z7ws+R`95z*SEmZ{+TjgZyX*!r81s`Pjj13%hWsRLd)_3sUS20TSFVxlPB+L|Qb8Ks ze@)yYpOT`mpTyLvhU9^NthmJ^Vtep4(cAQu3~m3DWVn4NxpvoyEb1MZI`cI#AEim% z_FN-IkG_+k0Y8a0uS178z;?Fk&>Y*JWOr^o8l`cKoS9gMcHj4k{Ox2w`~UnwjP@E* zD~GG3Nk%=ITK5&1e8ZHEt@=SWa&xLWeVG`;=W?T$WW*|K+A-w^IWx|dE~&XlHkX-E z)221#`UeNv`Pp|eW1$oM)czd#_1uQ)r#a9;fzL^Y(@khAuV>`ed_8)0l{O7+tw%eg zK|6jirKuYG6)Y_Z|^w6hkZ3Vc~G{QNQq`u7qIdRxd_PR8`BL&TJ-q0 zMs)Q^8P&hgh?f5RPT~zL=;3|UjioQTT_U5rhSR1^Y-#zr2x`00k{;F$rF7>}{OI$j9BNc(y3`dCHunlzEG%yXop&l}12y4z64Wle%7Y;~qD9#;jOeQirEoDR$F ze%R9Cb#}<>Tr#J=Cc5$tqs-{?UpYbJtIetZunqEh#SQ4L4Rz$3w;E8@NY9|e?}pUa zWwktft}bnFt|5QvT95XTooUs&r!HN7dbvC)UW-l*{~A*7o+hozZQM%VSc`_bER~nI z{30hdd<^Na_#4sC2t9Ov^at|c?7I-_iO-0J(dr`o!sldT^Tl#S(PLt? z`c+8tH1t zF`ywG!fB{UeHvOen0lTC|9M3?EofPn8V(DmUv|DEaZ|!+lWWgN*s&py()cOyEFMDF z2fif5VdLoABj-t3O%!!*bd_Wpj-f@1FOteGkyI}ao?W3ssP)7{y4?`5&{NyTmA=N(Q5KYm9VEFVI<#lIt)m&VavT$}Exh@pE6v}hYe z9KE5dK`pc)RXRL#x6B_%TckmMyD^9!$uprwiV?I|kuiPhJC>HZ8d8V)ku>ty1LAva zG!2e_O3rT`N#hUeQ?tKEQJdlQXn4^m`gwC5nv^`6UVQkCj9oLDR&;(tmTr%t_A~F1 zV?(0owB3(kx{0wgt+OsYv3M*sY@$UU>qXI;C*MgRiGslD*JSBY5c{`jNZ-T^qygrZ z)Jkt4y}rSmj@dncz9~2RFZ*@38^b^sjFbKQ^s#EctvYNJ1YW z)~u$xg>3+!FX6l{~V@){?(A6iRKt z-Uf(%O%w29qF>eomU#WpJB$8B^vqW{@428c}4#u3h~jeDFZJR zQUm-x=uheeozkIGy+eeF<8-`h#UZ(Q}jooo&XsEd%*2Rz!Lpu9iW8gg^n9!7s)e; z94ejXNa)$v4(NqMwguM?wog0B#e;rFO>py|KTY%Zd22}ixt?(5>htdV+ke_uI=(3Q z2KwK|N$w%^V@64SxX9n>Hpj!3(JzNyOJqAB`tRDo68GN+wu0>y10^JSXVK3V3ycBw z1dH0A*;}&r0Lw(MPXwZUEF7iUzu7~wj{^((?Ir`!9tlT=^=K%m{dwbm`#;f*g?`jH z>3HIxeet+P0Y?JSi;Ikbqr!Hc0wpARThU)S6-r1kQPlqM;J51V(>;xrb9PDv8FdTXp@-MJszZn4~bbwuh~|R(Z!XmHBqtzp!S6Jr z=n&Ka>q03VDJB_R4E4WZiS|X3qY$}NvM-UGhM0bIIaqX{rcN`u4ANaLhq~I8i0Loa zf=dy(3fzjA-g33X)xXn>u7-4*Yr(z&?CXJ>poHA;JKg98NKd&D>YKr`3AhzX$jy>d z61g4Lz%-Vam{gs<61h!sS0Z=88koj%8@MnrZ6_wQ#JV~S=}t(>xeMxhz_J^-7fQ%I zl8X~r@H>6y9x0tCrYGGCpJS5JLWzY^T2M?^x*zHX!Hzs2xkZsh(t3xWbP()^!Hzrv zF2Q1nm{d<4NHkmGY)D~>={JRxj#LTDQm7vVr*5jmR7m)V=|7K3#H62CFaMq9vl2=b zP(KMhs0vsGoCU-roo9e&p@cjKJPSMzCFBLzBkt!Sl#l|_g{pv z*u!P0pM*UiuRt105WI zZ$Vm5tXG2t+wT~ZkeEai&+`tPpPF|e0V&oWNohVYdFf-Q--RXG@4=q1ejn13s`)@l zBYGc}Pr&{F_VQ5TL&<&&S_a$Y7%Z!S)j+(?)v(0%Zo*cvf6Rgs5|f|eb*q9B@-b`` zuiIrPAu$;$_M59vLOzA9;P#%v66?>T1f`g!v_`tVFW^eofc+ZOkZG_+IxroG*WZ)R z10Q+Zq~S4>|JSd=e@Qm}&r3G&E>}NGDy4f{{oFV!-EDXW;$4n+HeS~o(0A}Iy92!x z`{x7bT@R(6guMlOggRO6D@Zo`=69l5OfLIL15?3b8d*#oi)mvqNvx)#w1?U@X47Jxikq8)xyju}G*bVm7`r!w$F4VEHs7C!@R~sG z^4DW}F6F7Xk=Tl_Jol3h>heTP`{>WlJ$(#wL_HNJpZNcWcE4>8=#Iog;^dY<-eJ9k zbewVT{P_!y2Q>QeK{3xIn0MOlAlXfOwfe`t-p_Rrbrv5I#$iFco#`=U>qdnl__drb ziaVO-R5ayh>cdQ900&b^K%;rc1|qI>Te(KVa!-JKhXn2 zR>L5k6M9_feDjH*CcZVJD<9!{=mF}u1 zqSI1;o)K;)#?Ck-77g;}k^PS=UA+&8=G%jK+wI4c0T~Cx;;0b*HK>WGlYL11{W^f} z-51EFZa6GHabKR66vT8U9u`}l`SL4Fj~x#w6AK=N@STPRtXKUq(K=7g70W`{xI>2p zOZDT`t_|419%bT&E8(RTgl(C0M4bNa#~=4IX7BXM#n^RVzs6W%Zn22&?9ZP>nzEBF zCxp&xIQPY&tnCYh7#r!&eVxqN{EP~rtxvhjh1M)HRw;D4`SWhWE!d@cC&l`=glkV~ z!=}9yqEzP3-83v&d1jRe-%PmGh&F6Zn^Iw%?Z-pkSg_}}PKli@3BR$WHJcW8RP21~ z%jVD8a*6H-OKRdNkA(mx(^H_UbX4S4j_<96$-}U}%OW$JAS<{CP_S9t` zUX_c7n}c~=cmO-q>4?~~%!e<3uFGty%S2}q!Yw|wVpfZb#G1N(eEpRGW_P|w1TbH| z41^kuqK=CzaIHhK^;xRNapAg4&J&_TSg)r=!t<~n57cbHTBIEp6NVA4r9s)QqlZPm zj{bb#LSt4jv`o0XB0M0Pv!e})h4E&8?hT@iJ3Gro_s*2Ji*L;=cPT`ta(}L&XTi>N zst`}dQLgc_4fB|+6sm3iw967LnU(QLq3lZe>1}P<3O|(?7zX!pU_-_)REdgrgm+oi zmbE@nDy|&$)!Unlsgiv*HL`_rM06d1xOOigX_yvBZ=OT76cGYZ}7OEg|gP-eaQ0d2c?-&X^tO zcvd+41|EH$T8bbjQ;qnhY5nf+-InjOUJ zNsV#R$gzG{6vR$g2w&SwVwW1Zwder(4m zg^-1K^HWcB*qW~=M8&-zt~uV1)wys)q@VES!@lXTEn~`shzaIC?fqHSlEY%ubRV91 zwH|BLvP?X062fb@XYG5cc6-*or)sxn?R%WlOa6++7~ zguj39BPN>{iu;v*{QfRo;geD!wvP0+ascI1lSvcV07PUG4n?te_0qLf=3k#(-g>c7;7ZHoI4|~ z!}_(?aWIk#d3%Kr*L-+`3KQ|8;H=1;+=}PNXVZ_nZ;$&V+E%hl?WHBce@70B>PvDW+we z7W)0={M`{M23;!>bIb$z{Wx>csNE^idykyI-zgVXOAm=@K7su1C{uAwt4i=wa(;SH zuoxbHP}suyN;6~O_^?tu*e&O)G+T-A#f2h95x}#K7>Y)9DusVPIhR}biXKh}#LgZ5 z+{MvfI1e}=lK%?e)|d6f@na{%L<2c5tkf0u^G}EltwZ?qgWe)D@_=}J(vLrht|xLY zoe-l(1atpp-XhqxP*~OY@(cHLM30;baUeB_518gHiaZYrHwRzt<6BQ`T3ab>54YmR zYkkC}7H_P5xIWbr2d117+WR5TqYCiVa3`zH3sb*wO2V=nU6ovui_9RD4>D&6jhsfQj@gMS>&*^1QYdB9fgF zMHYlNb|qp<_#ts(O(5@m%~Yt$PKvx%gjYugiw1fJMJ%kp&CXa9#Z`)AbHXog4-~gF z3dL5~{*A$gV$`?_VF}m0_BE<~9c$meweQ*5*RJ+GTl?D8zGrJ+yW00`?Q2*2o~?Z@ zwfk-DbE(~LYoAN)ep|bL)_yKAo}`1noip@<QKu z`QF01R3x`k=pWRjp4YP!SyT0C^^(aH>xYlUP^^=*$uwl1jLH*ZY1>O$wBPVabn3)U zqz8w&t1o^gYX(lFea&BxZ!KeKxA5o0sBe@4>rH28D6l?jWx9N(&wH}DcbI&*@)h}3 zHd~%^OoOi6%;jI}LSX-gbOmlFls_da-~S*{&90JRRrRQW^EKiz!GP{4dP;P^YtgHa zZQH^?mj=rX=!lxS)N-2v)qVAytadY@AJ)m}=iCN#$1Vuw_cWoWvfq)EPR4Yg)nk}n z)tKf?dQ3LWe?;1+yddtau8<>lev-VjE2MZl1j-M6LdG9|PbRO^C!QlV65C-gw|HIw z`8=Qw%^c@W8-CKHZR7mtoR6R6?%M4bU3zO49=Woi*PE-=R?mF~((>64MDdf3+ z=g8i1Am;eofmZikA#XPA0vY(qgwCt%L^~~oCDf&l6xC7Tb+>B{`IA)SNwll4uDN><(Rqi2Wq4Y}2P6S=>$sO02n zciL==JVaP6B8N^Dm*fY!P|d^XA?t^wlMWiCCA)XnQLCz_AzBf!B%$I&N#IIr%67Dq z-?JY~n(jVZk}%4Wo|&*jo^ic5@ojdkWN&K=TCP(i@7H4x`P!@j-Py&Gjvi-9(e7vb zQJ(fAnhbQ)qg}>ZQx~6lp6 zJAyFHn&SBlOR=KZZ<>1BQ#`-rM;lY@H$TofQ9Qq^DnS zJ5xNryk?FR`%TbZM~dg?w9KAjze(BBgyQ+#{MDFZzo}H&Qar!V6)?Xs^qVx+kmC8( zY_+7=Z~8>;BCQ%!lNjr5#J}5RvbF0R#oJzP^iW}vV$yCW8vS`QX}z>e4kKA=ILfD7MwMs5A(JYAG0mwGg+xP@YaCNQsk2w`EoLQ!~sQ*`Soc3#YH4_ z<9zaZ#s$R;2W@&){}{QxZ7x}A^IkFXLmhg5{Au$1+mqvaz0Abi2vT8#i5p#OeS69twhbMQ)!OAkCn9++q z*=9@o%(_K#O9Pbv2pLz7PHUmm96#a-JmLdP)3;^rueuyy*%1 zlf?eW2ePJeFum&@K;L~SC2h<M$$bpNO<&cI%>i%a&gI7;`}9x1eAh6)mlcQNeeUNkwbCA}fLL_!{aAjg*WrCiIOb{|khG%o%i zd(RD`EgggD%Xg*ZdV3Ap!Qhpm^OF!dV`UMEyZVD9xhoWHz6R0r)*wWE_kqmbw@{&R z%#Z5&PNLW@*6k)yY_DcdV=4B(@^`Tm`(2)50>ySZT@pjF-|e-Cq1aCL@#87>yPzpi z6x%7U*I0`EE~&|AitUs#ZxqFT*ROE|#pAfI9YOJWh7BA;@i<=iMN+(;d&WjnJdT0s z<0xLwDa8{gUcb&$Vkq7Z|J96^x@H<0<6$&UEm-=ujFy_nKA&;gdR`9|QGqJu_Gu0UJOGsm`;H zL}?TT@S`*eiP9)_21Xko&aRF8laRs|^@raE(E3y@8@fgR;iErFQ0LQPCrfptv5fC((rf+#PLMWG!9RQ?kEC83lb z$fK~1`5)C(XL<|(c~l_SQ4SRZ#QctGsuNcPgA6JJ?3loUNo2o;QCzBH`U_9VgcWM4g;JeBR?->lU7*EKVB!p|h5VDaq6d`1 zpxzVM1Bl`M$lgE@& zI0T6EWg{_xz%XEhB;7#)i#h?rNGL^s9TN+T1mb+zNK7m+8i?~{BgX>A0HdIUL?KZW z5NE?iV#0y(z!)eYG4MYIhytLAz)8RfKos~OrSXT@7!l8Zq zpdJoOv}2-*0Eueh9ww@21@+;uL^~#`!1^fYacV|Ng1kuRX~AHRg!YjG$3nkTa~zbg z-7vKXGFlSgMMJ;iP{%|St%0%7)6|>;uI6)D23=en2;1ODG`+0P#Ncgc33yI1M-ri1+Js zu*?9?0HQqsEHi;KfoPuvmPBA85bY=vNe1F<=V(s>ODZrGi1swFU>y@*ATxdos8C3f z4)xiNp!U+82U6Yl*}qV8MB!F~0+HnPguo<$Az*qSb<}&g>!dp7gh2Z`5MphVxbe483$sM11zTv}1ad5_<2=J^V)h8EZ4X2p|z0eJQO%zasf!JP=HnNTX2OlN9%z6 z3Nuuo@Ty4S5#V8{7lR-PiStGm199GHB+eUM0u)d}sw62E&cmz%8w#=1f-IbUxfJUE zcOljvp%l)@jKU<8Q>cYh$dh2H1Xe)_c}kL5pkV5hB*ZxbJPme~)gb@Oqx^3|tv~Z9 zUyuYomwpSQE`u}!g;D=rh=ubJUj_ShkZd6LOVSM#rtO!6I0eu?Ibc8dTPS!C+Gj4* zi=c<8c?3!*SW^oHQO30Z>Wbe&K?StWLZ~aDhpDNO1c6FeE(W^_+G)8&wNUUVwAd=B zAB82_k3;*ck+?>(qkIkP70}bvJP9Riw++8}3QE{+C>2DWkpzKfpoeXOI?jK*1$Yhw ze`;QU5(?aKW^3dnN$__8mOG%1vrq4lxJR<%?9+Rpeg&3jzXt7;EiqfN?}z?{^#W)$ zq*?@n^HHnkrN&vIZ-f0V$YPK*dedB$(0zN@I&iB02uL29!H$n+{8i@Dl zW+)+XUTVBgw?YYtGO?||?NCDEeBF3o?gVZJJ_gGZ;1eL)pMvEX@EH*8&%sgytdS%@ zIPdgx2-3s(s@3yU-vrAosNVvj-IG7p)To`n=g&{>f2qO#r3U*yr3T}&P+4nP8(CXf zJ6U^K2U$m1Cs}7%7g<+XH(7UCn5>7ar>vK(x2%t>udJV}zifbPplpzAuq<3QL^f15 zOg3B=AsZnZDH|mlEsKUGJtH(d|Y=3`jCw3H6ipsyec#o-_#2V0tl-~0FhfZwSdM0*ksT5s(J-G96 zUtzqgQgmPA{twOAz(T^NxmAiCjsg6#eGt3iUMY??0IipwEo;&BsgUW1@M~*)SOc(I z*?aSEr(D_8W|d-5l?PwgU!VQD`b6l(wc;D?oS5sRXTqox`-e_1dFpri!1amnObPgh zzOMHAwf9w9`&WDWwY7Zz(KcPII*1JmD@AkEY3y$!rp~Dpu9v*{r$`IYHS4+fHY~8T^e_eX0;#tVryD!Lx^Z(OMv$rI--Al47?JZgE_l3mQ)T2L?b*Xmg8!|)rkQ_YrntV^YO-f?x zQ&vYt?}PQ=5Ny3v#8dn{;C(#B?aV5Vr?_6?q?zTU4xmFaJYqTyp=IsY}; z6MC2U#(_U@kS6V2u18Hfe;tWjMP_GMr_23WZdPM%21M*J*G0p{S2=;#qB-WS4%NxKrd>ydk z%t%^5eF#xC1>)$rIZ!=ItvaCC8tOP}ur2W4kBc{vM#9xI66wI_IG&BiWDlkKKu0J! zN#DZ}ZcN~-9xu0pk{;BZVRRksGT6KN{M55DV`6JO4jc(rpI<#`eVkPnw`cM@kQDt0 z#!yH9ff>+1`ay`qY?FB2_yUF7*O$%@XOP70TEeJPLy6YX_!Ig8@I0O24DEoJfZ73w zqy3J+#_)&d)dZHh64i;d?cpr(c(7&gJoMljSg!|L!1J?%652g^0vr*}G5P0O{_lIS z3*(QZ{`3F+f3olj3s+J7C;Q*9&p-P@YOwy0gh9$B3I9WNfwB=_3BmSLWrs(#WZJQP zY31cqZn4Fc{V3^0vsz^G`qx^p9}a!!gxGZcP}Ne{`wpVhcct>4ZQMkwivwt6$8;Xo zvZ)B~)1SVtlgaI0dWwTdy+oV4Nqk=&Utw7_O5CV7gWGoY6iKba#l+wwe!0v`)Zr1a4Qi zu2|6AQdE;U{QF`vvGARpFk6z%mo}*@*6bN1Ztl+Hb9{`Ya-+ly?ta8kT#oe?eQh#% z(z6$9VSQWwG#>QHSvt1a&Z(SUX$dO9&SKQa6n@vuUF1z0EXG3W zI;)&FN`q))aj{n}?^6Csc_*{KSblppUpefoa`a3i@!4fAAMbcp`RiR@F~BgJ)25Kx z?|4&@^2A-G^2pU<5Tu`T8*{Pb)4Ba`r%T$mNEY#WIea|W$Hj*UlbpHI`a=VIic6Yv`EanC zoO2erC-V54q>2*Xj*W!!Mjnp{_Yog^#Yk}>_g8F4#A z%!(wQW42G(e(Z9w#x9MIJ9JJtRd1=75R=NC_GyTj!#0W!PiOI-^`0vgxtm4c#zfwI zRgH4Xu0>-0{xy^5=^6^xA$h_c!F$E&`0E4yp<5`6=6 zd3=(MxHNL9D4CYPJ9Ta(GS_E|K`o#ULtD{#$>Iyk=6l-ZDFZ$vi)Wu^^W4OI$$s)H zq{o8(fa~o1k-@upzm&@I$LV}oj+WHV8$W{-qN^K7<@^h&ytI8oseEE%3b$U^SSlCg zCiCw(&7|`8WwZFz1V^bXU!TP95jSzVachzFF_ph) z^^LldJ=lVuiK2^Y8t($i?Z)HmMEc%r-e80_OWbK8y7!yIzlPOeVbE3yHPFuw8Z*zu zy~Plk$@xQ9w#hR{G~Jxe*KkMH^QM=06OhRth1;{&ihiPIei~2v%az@O`{-=;oUyFGkjM3e4E6LbDq;>&y2*_s9b(3vWCXS^bpG?XY-IVm+1HJZpyc3eDCcR>`_{%7-5#i=XGqs3il5b zSFKX`s<(Z~lCP_TMSccf-G2bFQsxV8ox(N!Asvg`DuK_C&M)fF$361J!e`06@yXr< z@5jEWIXo}+0m0{$p;rzc;?k4g{n&irT<);o0!iE$Cd|jo5~-iuf+*z{JQcE-ST9Mc-S+EU%mZ{8gJbs-W^NiHO}|xo(_wI!^2em z)kwzfJX;_NpQP~aP7t-RWUUywb{6+{w_rX^R*8VCGkMP}P^~weCpy(6^XAc(tbJv! zu+@d<&pTVz$8o7xQkuXQk8)uZ@Jx=QGx$r##w<}MN960nb6~X#g9oC>YBG!8*Vraq zuS?hGa?AA_=o+K$!s5ale)*ygHC@_JoIjAqD@Jv-@u0CO z()#Vva`{=KX4EcXrI^_T(&j?>LBSsJsWh2?z0!=0>y;y_Mo#BH&6~4Nw&`NqoCF@4 z;|8fRGelz1G=50s#_XI@#hy3QdH!h+b|!CeX!CxZ5iFYWYlI zy)BEU-ASTnS|*B-Hrd?G=Zs|6I6Rx5+xi$%hRzT(?q%|B)|!lNnpz9>xa-df)_+y@O_{=zC;PL_x^ANB$|-!|-+t_FM=HK7kLNw7_%nsgVEX;^H15{g zkF{&;p}hHHI=4>uV><1;mFpo5=;{TYY=E*K%?L{2v&OnJoh{DFZf{fh!2KR9^J5ET zDWv`^-0#Eg%^W~mSR`?4NCk*?zX?e^>5&7IkL^j%U7gJr?y_NvE`-r@HzED*V>?#v zVXIW=XYtR+q5mX0D2G1F=C@ZnOJ&v((%@S8vS-Ia#p`i1_{VZzwyx&~W%2vzygJ5* zbr}~b)|^b>QDMGJRq{?rUd-Tu{3C7L=C<-&OfDZY^9#+RkCbma7PzfsLtbm{xw>gVBXUsWg1!l(VHs^#H-3H77 zv-elMSm(do@15_SbKZUTop-+tU(K(p*35KuPjye|uIjmJ*$PwnEFT-?O~0Ay^`=w# zq%XEg{l99e!)H(Bam{|Rd$H40wq`27wb@qj9aT*=RGrG>+vZd@-hQLj-IT<4zqM1w zJy@ppSd_#A2G}VNowC#p^^*9=??n`c^#*Nh7D9P`E2cb*TdU50Gnsp)=2dF#S)nFl z9cB00?uuW>^4juE2whsrTY31tlJ>s%Se|*!ThVLjYGcD#eyOL2va;%4wdsO!+_6Gl zg!Rn9yrZV_GEH(S11l9$mra?aO)*HZQOS^MJxi^=*Fa z;N~Pg_H!;}`SZf+rEf`CYOk1bc1kVwwm=fEkXc08Gp`y;>xPiNR|<%F-r|+SmG=3R zl)s$R`jeA*3&m4h-{8sP_`L0JS>|M=)yzGx4#lK};R`=3C zek~?QIo3B&RVobPC(Bk9_1W@PB2P#E9DUqZJGf>vckn=H%K_)rGGj;c9*_N$4qxEO z@O~r@KI*TGT7O4#k3cM`AB z%2BD+a;s|NK8bf=F3QOV#kBXmC-72BTon$vUBm=_a9U2KZ5NX|B-d14vO`{_! zhf=S4jM{$sI4)Q9QF3(br=H3h!z)biRqEatqJD}S&A&G=Dr1_DQ%jB?$(45jiedQ_ zbzRe;{B!+4k#4ti2p^Ogs1@)C)B?{9W2(mp!6vNk(#P1^^Fyxwmc%=^a#X+F){vyTS{^Jj~SX^BH}XoFWz z=09&0&=TJMQk@1(;mMf2fBf4wrG# zW+aW{Q%`tn5mW88{x8RHzX~3j-GZE2?Xlx{n7^xL98*FIcAmiVt#j2{E&HXWIZoh{ z?v~RAZOgCydOML z%4&-W4&w!O8?~%~MYZILBe-YKXSMIwC2Ez+Q~Al)U)7* ztA4FKkk7jvsC9feLtPv)nBV;psFnX&RSoMtn9q6;q$S!vewkbhc($ThG!P?7T3tD={Wvmtc&*JZfo{y$3&j=p^}hq&N+cM^>-2JoqHzo z9(T%WKfGJ9UI_EOKCY~g??rmilTzA_lY@Z?pw-im?TrKvfcf7M`qVML&|^m{aOH4Njcd&2Vtw*3R?<-_x67l+uf z$>XQ;fEf;2)U{7?`SwY?Ho|CP48OF|PaNC42%bOJ zR?(WFyuq(W@VOlVguG0R;e1J-;@Xx4iK*+FP2oFNloshP$X9a;)JomimU1NLFn<4j z1+DKMo;vsFME<;ZCGF+Ke5rE23A|gRm$uUk_91J3EZznH2)G?nv)wJDi_9TSQzt+jdj<+4m(y+I8h*KKxpM<^+42Z#Rr@ zU*e>-#`S%9X(F#zy`tFXqQeA!c($iVm+m=^zdGQfRlxpN&LNz-t)EB-1&`**r;K7B zwf0DUaC;T)`L+PnYw>U%J={S{M?3QGGKJqRTT-;CgUGk-jsTIiu^+}W(C4jdF;4As zb0R-_%}u0Ji%sAKn5Rg$z;}ZyV|+w<M83sq2565v@i^lMiK@5=I#{qE4^e1)X{5 ztPxFf&L_x!q(LLf=~qb5xSNaNyd{JH_N*eG)CE5v#CmJ;2h@gDkHmgKA(#mQ?R~+*SI_%ds zmB_Z?W+Vx>7QNd|z(uh2J=M%Jc$l)}ik_RdYIyB^78qxiT@)}Ww zT1-&c$p_Pj?)P~j=;(wTDpBzF3W73zhN?s@k9Y~{y|9H!WScQukZIKnm1vvu5JA3m z=BY%(Mr;=JwZmPN=+~PpLF)2-D$(!?s|BsxtE)sihhG(>ElO62ZmyatD7gJll_;WX zJ3#}I0#%~(k>v$7%XL~Mx_ag>K`*Q4QHhq%VS-BU9iS48-#S-NKErmE$S(Ahpe38` zt3>-xd=PZ7wXH_ax5z@sHMBN zLKL6RNl>!eJx0_s^OzvdhAE7w?CEuaniMRh5H+^76Xb02P>2R;WdtoQ{b?m zz1ybCMBVNV6EyM41ryPio5?cKluOr5M2&2B%0y%TylWzwR^y0F6uLbhBgzb~BFKNe zDPSXZq~b_HPg6%QqNOip30jaej}b}DHw$t=mLbKBWZBPtL`Nq9fx49bX;Hhz=f_Ehsc)3nNNe zcU;i8k#`tTwRdj>E%;%h5Y?JyC+KvEf(lW$Jw*i_ML*XP{oHY2Yv3thB=9t_4e%T= z8h8>I0XzZ>1O5dZ2)qxB1KtJ30N(&dTK%2@zCZX(;4t7rU_9_Ea5C^aFbVh;I0|U# zcVB@Y4*oDO6ll@M!k>Y>E#xnNLxE3#i9ky}7CkKexut)%phfSeIBpP*y9Mk8ybA0J zwES+--%>A@db))DI$^&nz%D>bIV|P+4EaRJe*mWfe*&ig|2_Ff{5}r9e*%sNJ_il~ zTFQ|Qz7Kdy{aN(A4taOTE!Xh^_zvJL*U57IEc#x?@160xWk1V#E%kW=zxTlJ?|`F$ zuYn_gmU^}5Wx4*3@OuJ&zXyy3TFz(D&vL$N_`Mr`x72U*{TZgDRhg!v`!Y=5S7w^r z4rG{mug)~p!u_=Y?yt{*SwMeaHQ+7mUlzaL!+xG?GEFlNXP7E&%{0|JnqkV^mT8Ir z{sewMmSNhyBhwUrJi}CZXQpW*BylBQ~7n7rq?MMrttNdrU2j^@U_x1OnWwD znzp4vKj03W*Kbp%X)wyQ9p#D#ZUFk>e2F+;AK-3ajZ+z>yn8ZD$N$VQt=^kydUqnj zbZ{5)g?v2Z#gSicg0Oc!<@@?6YY3g+*!xXU}^#yzbOh-LGM?LR5n_=2{Ak)+Y&pVCr zypsia&Xi2ke((puJ6y;xIf1u>JU{lg!|`wTVgJ)8H?R?omvQ`Q$Ww6Mk0`$%^!|); z*THp5NB#p*zs|TWCvm;5puAg9-qk2iFO;VP@`*-1j=(ZNTc8c_DbAOU^L0XfG1S*+ zV^)mv;$Kd#b*ncJVzmM~1IL}WUcLVu< zhh3b*@#)ZGHS}16dhH1Nal>(ia9lKwtBvE_j=)Z!{|Fr47ROJ;@f~seR2(0L1^X!kOoUzEfL+f4W&xMuJZ(@9^`OT`oaYwg4ECRi^G(P37T|n- zIA0jfcOT_&#`)IbJcV$+NzlU?`pkkIc7|P^!EsxmPdLs~1;<~&aqnTz4PnnJj&A{d zqHz2@9G?%zkH_)1aa~=yRXAIX`W;4(@zx6Bz z8sDc`4CdvjEnb2&OMyoDX^sMo@)re~XDQ?oUUelfDo?WnN@A`GE#K=PupE%i>xl7sCxJAtfky9Xz5=0(kki<{8+d1IN$!fVec~%( z4gn$gvv>&f5VHh4aTYJ|6>v16H++H2=q0?SD#KUG5Aw>``U8#djS3JL06!_3l|g~c z39Dj8!~b0p%51i1-F|3rofuQiz*0`+Teq*B|Xi7!3pz<>rn@#rTTP%?;oKTu&%(m zqWnJK>Vfycmg>PD+xo!zK#~VQ(g4^1Nb*2Ph;N7#VGUp-U?X5dAo(aZ1~vwgyf!3F zfK7lT&krrlUP)EqxkcWNwFNc<(th^XBd^%6sXz{W={mciwg~G0sUGWKOXa8wqU-nC-E?kW<)qvdcn1l9P|4*&Z7TIW5vnc3BQc@+Q#QZ10VQycr~9 z_r-uDr=amAfF*z=&kG5)13Mtei@;Bfj z-hdQ1oxA~uz`u{Y0BHef^8T|db3Iz*|AQl>tKC_V`90(t#xdP`3eda(yUQF|Vq*oH8^m|gax7}c9Nxu(+ zZ=D(c5Z+J2u$>P%g|S{JaG{WsPvRo*Be5m6spbp@KsHDB22JmGgr z;oKJpJD!g%*)RDHQaf0HE!j7%6ixQK5L=Sdy3y1=7GX}EE$RE}l%g|5>wZ0R`iOAo+d~u7%IiI^a6s7NB`O>)Z(OZVvA*L9!k4ow!dE?!uXO19t=I zyn7%aey{MKBd%@FgT% zg^J_9}jlD#~`_Brr5km}<(wl9D$ zfK*Q}u+0Q!0_pnfhSwe8OGwPPJKGy?x42%HkOSG%Mj++05!+3`O+dy#boTgXgyS015FQks>q) z3(t*^;a?CmJa=}`@Z1O+{uRN)bK97Mhv!BJaY?d~U=JdQIA#kYka!O8wg@Ml6G%bD z&1i=do)ZvYoX{L(9M72uHl826IoLR!QxJ5#Fn9;)qtL@adSg=r{A(PM5BLTt!lDRU zUJO_aNcnxSq44B{B_z_<9H^X-!j8==by%OP|*AP-w}KekYcJ(JybwF5dMM%NWf3H z`h)=@csrhl5z4(9_^KkfJKhfOj)3I<&0;tO2C@`i^qd6j)R2?~2-` z-<=T(-i#Fx5}tSyB&0`q!IzihY;=?u|;xes#fD#+J@g16tDW!EjVG zqX`a+#Mg%e??=$GfxrerETnd}B!P{sb+X2y7zcwU8_E zHIYA|JFa$9U{fIJ-4xqqz-GdIlI*+;93}~y3%%<=s}|tvU`zXvdu52g5FsaL%TVz3 zu_ZaVUWN$_6LNCIYze+0wj?LF%vJ(h2{}1wwg%rATaq_HuEf_x{)C0mD#C%`K+-!L z+X!HUaP7qVGTLb*_%hg%-p!y@l)xylAGv=*a?~;N{B?uAP^RTg!-w%QfFXi zAeE~#wq1Z-fK;y1*sH6+uA*EK(7GG=2yAITa$6<74ct`C*cK_g_rPh@jP2mON_+^jUWoMVi(bi$W7jiWWObw--*p{#Pq&70&i*NWw-uu z#Q#6b5x)=GbPQTf9QurS^b7sb_7kOn(jaNDG(;LI4U_(mhD#%)kv@}KCy~orZh{MEzObUO7o=o(gJCrv`AVkEs>T=%cSK}va~{4 zDXo%LOKYUH(mH9qv_aY^ZIU)iTcoYhHfg)GL)t0rl6Fgbq`lHUX}@$pIw&2IQlwNV zO;ROI(xt=F5$ULOOgb)|kWNacq(7z8(i!QjbWS=iU63wHm!!+m73r#UO}Z}KkZwx1 zq}x)ubVs@?-IMN152T0EBPm0AEIpC_lAcP>r03ELDN}kWy^>x_Z=|=4gBYAz((;xxT=^gv9a++8yoHjrVyWqnsl=1(Pxlp@uvOSozMVYFsh2_yx)H9 z%D8&`RnJmv-^l~ok&6M`r&KW}^*W&CpH+{auq(vs?X%H6X4m6glZvw8c_e+n=0K6o z^D)2P_(N5`=0`!c<$QkqK=wZWck(-13$iI4OX?+KYw&)h^0F84C3Ow^x4vl4N)L9{ z!@^~LvqmoF`@&g22KlZHd04g29(rfLTKq(@oNWGCPu+G-J^r#rSJQGEPyOx(&JCZ+ zm^PjB)JspQ$78Z?rd2NNsjpea`Nj&5(ym_d)Q!XI@xb$s(q@i!*3bP>kM~${D{YjM zv+j4!#7mxil(xpJq}~?t?vOY0D5)=cQ=NZGdz3akJiq>Un0f!q$@z6QtSTS5@ljgX zIvYI_@?O|K&R)`UR0tI3qS#&#Y?2! zEofu~HbgPv<7-4Q;@`zLV;_q@my17d%64oo&!#LunEQGLcC>6m)_q`c)@*lu_U23; zEFn;zkOpzd@sXdZF>h5ULPku3(q6oG{!50PQ z2Np+)&YKrolGD@=!g4qh9dC;*l{p8H@}ra^w)Am{7P zU*E-}c$t0`6wg`t^&Wpk^7ofrmBS(V^lF)ryxWEfO843M_4zF$`A>Uy`o?yV zeBk8@$`{D5w~63y=Xod#6Y}ZzyG8J>jh&UkbMot(YKHM!4Ly~P1M=y4>WA?|ojnvD zlTSYo6UzNVoR#f3c2k#7ek5llCBAJw{o}?EZpWRK6zEWSbqIg$?V`MA`Sh*jLU=|A zXQe9ibS)dg^L}?$mPOj@u63gL_X^I+tj&4#S18M(X3omeEqV1-Pa}E44mTyRuf3k| zGLk>aMgMYp*|!j^v-VJ1c#*<<$$fi{SnGdn);++v{a|MDPrE z7iBEuPlCgER3$Iv$_#tGPJ=LBgL^8wC)w*CVng}%$}Wlv?b9`s*LqY*c|6cw_uCx8 zCpfw&zR0WRnh+ji@2YfbW3NAR4B?$WIx8EYXM2>T`BN9=XiGc2f88j~3b-iy*5%P< zl;uth7v;$MJo@`*k$mscib|UvcKVT5k=!G}MftTMkM0~E$@ME9%IFbxdSHi0UVW*H zGIL`dy<>C)-`&AW@tYonEJ3 zDF0B}RcS!`bPwg@&$}!333mF(Eg}3*=-di<9atN}cYbnJl3Ls8)ty54x(6=GdFZ(f zWjS}uRjC@6OW#&6imNYN6wf2M^}#4hyOOR-(9zs_7ZJ&oou10aMY;6Yj*Z+WC z4j&VxVJKOhe7vkjB?ehs4RiJZNmueWnWn_r5N;XEhG4{w2I0e$o)LRc+pgZ>q#@{ z(*nZyhig8{*AH`oSuc+vEBz-i>l60n`a^|k2SG^X= zyZU-6U6^&FLyJohENd+kUb*Ud*s+GC>+ZW76#R<5KhhJ0NO zqi=5&!LO~Yr0jxxsAm{&vc^}jJ#3@j2n^%iYGvioK^uKiyHLJzb0sAd`wR&S!aMv{X_Fy9KuJs9AAHv(kxGSBZX9UVJHq=A$H+<0|0;2eULGDV)_1`pSl%>cTcg1((H|_e3 zNS>M7TWRm{MO%L-l1JZhSJrR-rsb*^$+Ki%WlQ)MtyJ?!9-G@kIkV-PR&ydMJ0>XKDTPNba7~M=5ykgI4Q!BwuvPLkaJcr6pI6KA&3f9Kl|S*VZ>$EypPSp`)i#xaE5-U%4ngYnrFx(fYlHj)wPs z>!Um3^yqat`BFMj4gx0&g{kMxlH|jF(b{_EAE4O&339 z|MNFmv+*H(1axj1{$86fB7}EqONQ}zVFAhmhnLz;moT2# zw~DeN$4jkK<4~R%<*f{Fj(vD2A70R3v3-!KEt?R+k3i@7OLuEe=GmTzm-4d|4Ze!)c=3>`oj){{lV^jv%g1wD}U#|mA^L1pKN7+mi+%`*Z=SK zmztgblE3AD?{E3T?tim?%KtxN|9`i?fAasm{U6G%|Nou+&wQ)?=Tvt7|CRoS?C-zQ z|NM8z|G)aBrV-wv*W1v?OZq}X)17UB^u30@K$yQXn5PZW7n+vz0fHoUI7S-KjFr zvRE29T^Ygq3CAmPdol{QCvw6Hgq+-sf&`k~uxN?2YT#*+wCcd>z#2euiXt>m+95Y2 z6DIFq`k|P>V_qCh25+9oLuhuJstv3Ixp`_2xe?V7Zbas3K4y2K2Ke1P<%is!8erlN zrUPQ~521My5V<=w#bh9I7orJ4QDs4cJ!9C!#VZb9v`-5ffIiAI?AmhBEiOP_VYe&(r1 zg36PE~S z`jXk*r@ugQ_%SbjmMAb0jw|HWk_gwDf4Th(7LGRLZZi~)Hve)1A}63>;Av^C;lMvI z35w7>5sJ{fnAVtobHAafQRc<9#tSqrrbUZuO$0vyTXN%|#kD2_$vKGd?~7iM`_DAU zr^2y?kQTd|CD6Rs)ogHcz|YQh&zYO;zA_h1EaYZG&NmALnisnwXPZUfX^|^Ja#L9X zTnxt(LUK!423!ia5khh*St*blOUR93B^*fzSHtmx+)GxA#c^n5`aXOpc!%i>ns;V`fh{B~@20e1m+3)~Hd3iIMjdj;-= z0|&X6>=lbwS=>#?o#Y_o`{7!k!e+b!f_0qaubKQ{97q1$-9i4#)omv0gKyk9vl^=H4WVxNvGv|Y$^ zv6xiDm>oj?vb0@lSeKnb{wy=m)aK1*Znt_nFSTl-Dd*`8d`RkcK5yD0(~tri`4H1~ zUbe^<)6A}G`9;?uyy;7WoH>07PwqO5pC7#=#ck#$9`Jq;&)p$8bzh-P+->4SK7I4` zl+5W{MA~{EUifM7;*>;D4|TAQC7tnD5B9PDrPQ;X#81_Fo#I?^htOx7Tj`WpMR$sH z=%JX@sBAr-F9}Xv&|#a9Ti1zoT`gW75^Slr=*eSKb7YsRhP`8Izw7HzZ##GmuL7y# zQ#SCSwUM@xzxXt|M&|T2(DV0wto2Fqe^oQ=>Sl55*cUZ|EqY#F^DEe*zjeB{X=(7D zy5{{8&($$m^!EyX6Ffd(lQ>WQJ^pg7Y&)6J(@9RvwkvDf$b!A~Jh5EV?WvLvW@j9m*o4YpjD9D`Sz_Ge$O?PYSCJaHWle;D-J zwX@#edY^!W*Mm3z919($@fkrUQY`5g;}@7-A*>(Ct@o*lyrx$hiu#<$*Y-guijR}U zKF@RiGUdBJO2}t#FDh>ipCRP^_qxh0Hq8_1VyKf=S#yz|$iH6e7QAJ}Vqx>LL%o#E z*=g&%rf)BAD*kAR*vESR=5dLp{`Hm%`JzLMOqtVHiu7MiHkq9Jt`+G=NVDuV6%QSn zg)S93EW`f4vfI>T==pBVDj}zH+Ge+@DJa*@+#7^^1az>p-(#qg?XX*_lk?Eu(tal{ zxE}oG=T;%Nw&8zoA8L=a&<3eJK1`lsdirdoNJ|5wODaAg`8iP3HeI2msJOng?t;@OYB$ZiSt|c3D*69b>Dah zcJiRbTGZ9V-})cxe#yGO{d(UfZTf_AyilVFe6jy$(6H=V$(`(9;$Q7)4)n19MO znp@kab-v%)C#PEM*7}@dzphWMi++1xuZcXz(fO$pa9x@^4f!pdw$NGLg7&y?_y}I$ zN#(Rxj|TGshSA)*9a&#%>;9??u4BvW{+X_?r4N6C>z+5epD&O&FV)gNhh6=VQV#v+ z2m``)Tj^$9pVs}bb$?~u2CeJLx_+$t5NkX6_xhg^=(~Dn_fgjMV10kw`s!^`$1?&AsB{TS8T z%k2KPBc2&7{hoE7Y~4><%dPLf*87~?viXpuorb3;rOeH4%QZgTOR==~M!0YG&%0W* zWx8+j?Ec~Z$+6aL&Dw^o+m5x(ShvSu=xMpP#zTM0eYrezw%l8-ZQHv4v3}mL-rxG1 z*4K;Lf~D-%eVTPYX}yniKWW`JTHp5-ejj5zw{1F>^Jme^mdU4UHDLw1O_KY(Z^OFW zoGH(5?kVIWZ-11dnmjlCD3~QzPjxqqpZ!rz_j8xKr{rWMeLYx}T)9~Da4(iNCx_tM z-!8zq4EA7Wj^-8eGau}QJUnk6!5@2-TkwkuH7|tt+!UZ*I$` zl0NLkt534pV~5U4hgY)w=7%jqon z#F>UH*Q(C~o88Tl=Pw-0A{yDTjuX8ZYoA-7eZPDHC+4viXw&<%oPT{8_O$C~dHj{K ztYjWDmYGtJ^*`dxwhb)IDmzwYT^1H(^S=5pKZwg_7tZ^kNV zgIQ$!JGtgpU4C%hlWiEO3hcD)xIjnGvjU5rydtpkpXmYzT|F$$tIx}jjUzpoXS4Hy z4_NtJ@Ohr!6#R%>uLWOy`2)evZuM61^EN*gJYCl+y^FA4wq~UAl0MV-UlH~-;`UjA zH*%a5`11G>fjN_OfppxQm%hRtwv_Z0_OTV%aMpGC(x1L;eZH&0Ugea_f-m^zm>?0MjdTz`L%9J%_de82H?(;CmO^3nmbO*t}`n|xbLllO+@ly5E_FL$sV zBX26wLyrITNgi#}fKj{k=|6ytD^pXnXU`@**;V_6^30#{tXREea_}3+#9n}Z5lMnH){^3ViYDp}=_! z3bHzjyjawp&+^$bXM&53{34%BaW_$WbUAw_nA-8~VOM1r{e`gqfgzvd9CPcih0&kn zS~L5y{Zl{6S;*IBQ+U$XxzNR)GLTt2pV^FjG?PkVto8y6CI>a&ArPacbk3Et^?8G&jE7a?!h%~|l& zF34V}U0=;xnPmYwPm)F0CM#>csc>-+(BOZ_9&Yt+`yqJLapv4H4b8<)y2aBYb}tgzh*xpwgj zqP6Int}>JNuPZ{jJR|LWLPvw}7V!11rKE1FkJb7?K3R5YLX zU$clUvxjNcFpXeY#-J=?PL_oUEwg_qq+dDgmlq>Q<$?LJEh~;Jh|#WpjVa~B9yIH> z0Jd~y%a{_4B)Nk(f4!x#B2O_^Wd4ea&>#4cOS6{EUvnuWU7#2f^29e?Z^$WhpD)l4 zUwCOhv+MT1#(v0syAfCPa)rEjEF{b z>H%9IRUa6V9l9$FDH_vhh0z@v%Lzw{#&9B$iUOM5z-g9sG>)L-%^}>HW6Y)l_-4XA zobn?l@qdlg6#lm{8gnSNo?7;CAEQI)|sABFB4 z0vw837zQ-El+*0%k{Cy!&|l`Ul(E?%9ckG`8cT`9*hx4r9HS@Zv8svLV_1_=VzQUX zxT3i+l0u^nCE_0#jJfJ_m=~H=XV1%hAFbSol{EaAW72tf7n8sjoLOKwb zh#+CPfDLdI$rp*yTXTr865xwtG=^{~usDzcnh`F~9-9gHx3QV!Vr+5+Mri`@`${od zLt``T#8_h&jLmd~+%h&pBQxeP8uQo;jm((GXlPW1#%4APq!F4e;5TDrhLA>RTm-s^ zF{(+h9IEfq2$Ds!?H7ZVfR>^qQF=M%{x1b4y}-RF zC5;*FM(JoQX(vjx9k?CZ+XHC~h{mXBB#6eOC=i)>EK0?EEDC+5i7{p!*ZDB`!@wiJ zBfz7;qrhXpW59~Q$>?Xu?sDSF(seD3kW(js6f&+qupoLP>US!jMUUP4_c#eWo*m+B5ytR`gLlE`wj=N~@;L)^2c89<1)c^PF*;1`X%t4@ivj~+DS^Oq z&?E=2HpV~)0Vm^3be=KLZXqxiQZ$CR3|2+$EmVvqQwYFN=uCQt!ZvfG?`WAlhT2ld zBOs{(c@1C!Fbcg&bzpTM$%A2cCZGdaNi?u9MsW!#0A5dl=1_MO(6Kl8_Skj+b^!Jj z*jI$I>nKKZ5y}N)x}6~Jf{R0Qb=c9hLKKfJYS3}V=Qoq^~)*cG{ zS13#x9jEfss5sSgEfFH5F3wULyb))i{@fpDF#;1%dt?V8s2OvA+a5Vk|J?yOQh(bK z8c=`R2^vvo%pRyQgl<8wIjXmwz+ONa0q+B(k?$Dbc?6-;fpcIF*MQf6*MZl8H-I;Q zH-R^SSAkc7w7)G{;R@gmpe=A0Qq;aEgder9eMnLJssYUh0BM#8weJETYTx;gqV`Q8 zd8vJGM=BY(6RDNJ-AGaU-is8q@BK(o`(A@~N$qnT+9kD53dOe*NMpg~_PIs$FXy2p zUGKGMsnkx_qoqY60&f9t0cQjKcr}b?(nyiz+2x{u%%M?>hebk6!=S=6^RE!0q@yJZHv6ajy<%^w@;1n7~jD$wGX8 z5gVJ;QjBM!TDO|NKBj*NKU}M!+WdmAp1Q3uzc<@kU0cOpFY~QAKXc88d3Q1Di>k)* zKOTFt7oCmziNshw>Rn#8u&Yr|_!`55uBWTtZ~5xG*TnMJd@t2DcYJk+tiJry=3DCU z^?v%Ihkbc#uXbu^qEU~GZNvGc%If3tt0xv1 zz`vR9r3LNw(Vx2{@`YpTt8M=D)kg>S=aW94VJ|ED>%Y3h^1#gdZ1x^sJ$Y3eukL<~ zwJ+GOwL^7Pe9#Xe=qH{&^q=pya!7Q$P;c&84(>#JYr(wl#LWT)l7 zr<9A=VeArl#_@1>w`_9ywQ4Jxee-YzFP~P^)iphH%DA!9?_``Yq#1* z-_yDt|0}(_kS}S_gb!bNJ++{VzuwThKTpnKmsTs)S8vJ(@L{3L(>lBR>p5-ubLSNs znUjrCZ#F2Fzi>Rno~-cKSMXR~Hg;Xw=@_G4epWPZG^IwGYe%ErIkg>kskS1mM_;3E z6BEt7i;j`M{^hTaE!c|Jg>HtQUb_0Cwa}T;cP_W&v2Ky9Vk4t|{z50-{ukC3s%_L? zT1h6SepuSm?nZs%gLpo&W9u~AO-6mwF{D#XX{$FF^|6oR`NGW= z)jwj5y5FIG+|9nXnr8IZ@AQr5_3MVHgX4|*ZrgtRYSk0!8pU6~dbcev=P_N~JlLo| zc-xWNj#!}9X=cw#wrgTyNZ!?do;{1aH`>g-91I7{%YE*sjQZf-9r^9B zylIweLFrM!MsZzI>?@~5I2-k3@7BEjI$!39Yq8)}ciy?U!ur-T>QPO*^R!|L^G5o@ z#qRu5t0$`Ip0B>{4>-u>$*EPl;-g@E%bBaE7mbwMHj?pi4gO5pquN&Qq7rE%gio*tOTKB>UDbb;e8udWy@O`^_ic0|EAB& zwZ1F??ZUrkFFxSj+q6n?Mt%LL2rm7MPpjR>sONQv_^sUqT^y%|3^D3QqGP$MX{@?s zDD1OyEZ^47Jx7CRMtt{4MoH}Q+ z$oFQcP=2iQZuP0`ueUiE&L1Bw#YVL=>R(UwQira;;|9=wR*6zQnR}8q%M<1zVnVH@NF^OM7s`-8^GJwZ7k$D zbxUU5V#__G|+}V?EM%{6C9Pc{Ei@m^o&Eb43|FQdO zS|IK_8*LhMHKmU!e6x=}*ts6}=spM&t9*b@^3aJ*Z<- z-gvra+D_Ef()I0rvn6YrZ~B_CvS`1b9(Ct_UCeFpXx+|Or}HhlTG0=7+m)Ay3uF&r zpKW*b;c@l5h`iK^{rK`q9_%V~_L&>Uw|IoJi=B*mzJ_u9>guct>`y-`9xez&TN?6=NGPoA8}$9_pUaA&=b?$$DqpWaeZ&a>ZFf7pEh z&v|H(>VSLP%dcU)_DTQ<=u}br)J7Q{hF&d>%gm5bW(@!HR?O;`tm3KU(~u&(cjkT#M60_Xcs5H#PjQkBjgejao?E} z|J!|`(7dMVlm$k8c2HOT=0at)`D&w{IG`J!;{y2-qu!}pcP>TVV98(nbidLu{M4l_ zs@nm7eNAQzzv44lEr&Xf2#w|Ya8EoJYSiQQhyT_F$9J7%x&d1{oEXk`e#phf_dq|^ zBlb6Ywzl&UlMBjY9R2kNP5blChPS5LbBy}8S_!|E<)|NtTO7846*$An*6FC+HZr4G2EecfSQ4_6x|cc?`PlF==ta3hCbpRN6$qhcfoz1 zDJlpVWQ+`HHNS!`skHzwBSdFeiZv8A8F1@tsWn(MFOJu%wIujdd4GM5Yk$7;_|nvuD~)>XCJB7f zK5sPy>HGz{@p@lnb?!Wr@pLTrUQDVGS00$&!^YB}GueYS%lw>t7HPNlcs|7Qr*+%!ntoKKXZuD019&}Sl6o5V z&{OW6_={0V>Mo@3A#JnAgLT34()|8ic|xkfPSrB%?>ltitFBd37odGDofE@d6N;)n zu+L*PV}5Hdb62iWJx}@QM`kwQ$NhV$4o7_S$II*e_MA-D{nuTyZZ$_vRO_Di)n_OP zzqQ%ex=mHnB0PIc?Z&O!xb<_Lwf$55Tdu{GjgqL(r0cQ2J`yjh>g@`o;bBY;4_EKWXWxZo)O1*03`_ zYHFZHtiiL5u_tdn-d8=n&8QDO+>;kZIvvj#6CHZ-DWRiGb)5Y5mUEl)&f|R5Z>aOC z3%YSP>~njIQE%+u^S5`RsR@rweKU?yvX5bLO8VF0ed!zi>Gm!^xJYuBd))+oj9&Owz7-;%|MC^}B=h zyNUJnwXU~e_DIe7z*L?NqXS$2N#d6*2r`xbnc*d*zI+kZN zUZ&pDee`aH`~TLK1LrJb`+`R;HtHs%t?yYUyFO%3cl+soj_kt^jd{jK#{20F_Wu6-FWK6Q zzS_MC+DovSR%936bEovUC(uC%re?CoSX?zP^R zmF7nMPZ{nqF6O-r?EZ5K)%rCASbM>vk%69i> z#b&*bZ*L7`3)An&cdOT6wFcU;`p;L%E7rxZ2Zh(kp*g#=4^8*UqxBfJWm|7H?*wBG z?PJ-6E_GNnxfkodpcX4$zAKA-ctC#gwiA1Od!p?1M^DzO{&M-w$CB*8J3qFxRuOi2 zehqfo)`8_%5Xp+2dx8@?29J_C7IDo*Xk@?y4rRCx;fuA7;d`-JUDtikB1c zKHrsj6;#;9&OO-RI*bi@zCte1Q(_*47t0UUf0w6*uaJEo*|6SC^0FVzQsj2i4eV2? zKLzfZb5LIYAd+o%E6j3U3unW93bUHiIxr`bfz_$LMBb7xh*gbRA}912%-#kqmZQ55 zW&>k`SWv$P%&)v3TbR2s>owBIex9t$0_`fWqd}kKvz@$H^%wbBrwtx#&*1_rbm>g_ z{N@e<>km#4z&?&Lvbfm=*sfPT%<5Zz~~3^#{Qw~!{@v#cMs7!C=B-t<>OW zGmgqT^0j86zw}DcZ`>+*HzROOn<5_JbQ_dO| z&)U{1${yO3Vq3lxV3Apc*}0*1Od8UgE&twtmA%!C`7Ef%96iI?&oD1Gz&@Nc`5)}P z2UHbJkSNT>fGD6ykRT$MNFwo)r@PFU5yb=;0Ru)dhy)3e!K@fDV$Pt*ogQ<}Ip>@( zeP;ja;qzPAv-@_>p11GpJOA?>sp`IS=gxFjS5;S4S8Zxferz8|?9SMeBN|F_)};?w z@oNOJ>)es7_~JpHT?KOFojq}^*NHSpu_N_g5whMmfaqNECY9;_#AA*ZiB<*>n>Ai! zLh(pp_AVC^?KDz|H*_XyIwJ&r|5AMaQ%&v2iS?c&@8)GebfZME;({#KCW z$_J5WgY-$C^@B*XnIYL7J%IFj(Ui>V;6`5Cx|7r0T}YdaZp0*6NoL%2B6bNrq@LGo z;ljmc7_6#`bgN57=$ey0rGgL}If)!U)>oK)B9sh0KSXZd|5hZZ&zwY>*5Bzp`(#Hl zcjFH4rR_SC*VlJ=U+M>B|0gf8Dm?amPpkSDO;wsnf{nXj3w6UI6)Y!j;^7 zHk2s$bR#+u0}1KSnH=0Pgw!+WM0Q6_Bkh9)a`xCH@>hrlc`DkFjiKF0>5FWk(dAGw zE_Sv6H$w>t%MlhNP9pn>HSzAXOE8(%mK=KSOYTJWC7(xjCE*A9kYI_oyFm}qn{*|a?=k)A7W}=}qQJ%nkj(piNQ?OXxF&j% z3Wd@uFYk;k|G##nO5 z=9-Z3yf4|cxgNQ7z=ssqQzshcUL@yoeX>u}jr7)VBP~zke6?syp7!oV`snl~*Y@@# z>hpV&`0G6h=LhLmok#;s6Y>rDzj9p*((Hi~*>7M$mL8u-ls`Smr}8#rf~G)*^sysT zQi;s3rnQMBoG)9iiXmUR6$qwZW5}!?{mI2kgpArWkns8S2^&cGe0RT?BV=!hAafp~ zeZ(k^v`9`Dn)t_(Hm!rn=`uSZ@@FXVs@F^C^3sLO9x9NQB97xKAL426N@CwpnICcf z&-oIU2QQjG_vY<`dg;RSj%ws((R6{o|6l1dg=de{h}Laq!ueoS=Po2QXO0lDq$AG?ZbyWvYB#>~t3yF6?4VEVY&jhphd`Rn2+A$JAhQ(y&iR48yZ*oR&AbCIAnP~hNMLc&jArC*aAXC!4Ne`bqq0}UfvQF$ER z78#~6Cw_ifGF|fd<8tWF^YQhxQsbOp8@559Q-Ex=*&uNK-ZN>Vz~7hiJ3c>u?*C`~ zeOurluOIv8t>yQbvw59RFTFEaxu#TDUxD-6aht&9?2GhG0$<1JJGKhFA2%fr20D`o zr>uzQGFNi%p$(x3S8Lf3v;N)4+#Pd-e%RKwGA9DURmZIe+tyGX$74MPk{SJtiGEry z!s)avX(YdI`H$XY-T*&R*WZ{lEw?3wUyMnM+x9p{=EU=h4(Z(~oLrvZBhx*f2ZPSj z<>&rG$1kUKBV0eqxm_t_e~loS_YMjBE>0qLZeE1H&-GQv+ZTF~S0h}=$T}Y6?whAV za!?q#Q{I}C-&!biJqv`(^FQMnHC{{R^L58*k#@1pWd2w!dHpQRG$L)5x02_P>nRiW z`H|a4alBuj58O&#juPI2VxxNc-!%QM2+)sxBe#;(Nw(-g9Ef;}0owL{qQJc9H(8%r+e z5EA^`o2W6It4&7aRkJoE4)vCTxkjW5!f7|m$q~GN!#5TrU1t(mcC`ul;vY!9pd2o| zjC$C9BeGrTNrImk3LnP|AU`(Y0gWgll03yyrZY~L1Mxl!CYljWZ=64z)jH)yQW+9`?@f9pav_}LP@eL6%Z-kET{Y^mdGf#0WFC~5SeelmZAFT)$xEyUCGELz6ADa{{Y>LMe zu5`Pc50>ce%S+;QT_`zg6IRN6(#Gg~|Qg38xPy z+el(nSBpHh_aP1IYsvgML`_T9+h$LU79OyUq;;PtfzzdxeVoAMG5;Qa&;RP70|h3Z z-u=&^gFgwfc))H0{Ld4Z^B~?ohYcEIm>>@l{F93OPwH`=aNGp@dD<#7g#3s>ON2a4 z6%R(_X{mTn@4r4o@F&q!bs88RB+3)P@Pty;iKMEN!SF;;JVg{wY$eF|;fDoya3inq z&Vy8|tG@BXP`vIN4PxZuuN&Ct{raWZTAKN^wQvgDK$YBsdp1R2p{|=E; zH&rKW^20U{*&UAXPr@cA46AmQ`-jU3n|K;59!wsJLHc0``4NEXpj4iMiPzEN#|C)a za32286F5~TVA8_SS8Z%}$52vVgwb+}tba+k^ry}`KLo&oeUorB_2lnYr*!wm;7m7}m;z zRb6n@N)i4^bfk$-y&2m^7_MrJa4UwoZb#^iZ^eUOhhvEBE`*CQM3x^0(8u}SgK)bX z7SHQ8kC77@jlvP;p|X4&*J1GODTJpnoSf4;udaO_;a;S0 zU4$1fg!&Rfd;A6;x62rQ%!7~p@Xq}a>S6dX4?gBqsc#{?jg_nKBD{yyogW~~ReLBO zlF0wNK6F!jlPB1pfOO}Ca5C0)K7g<(_VWAls>?4BuEe^^oSuttHeVrJhxL;y5dKTO z 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 aWB_CUWB_CUWB_CUWB_E~cQQan>HUALlc9nD literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/fin1.jpg b/Graphics2/Models/Plane/fin1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..49a7f44c19616417d7c4ac1b755cba3107246abb GIT binary patch literal 19969 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 literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/fus.jpg b/Graphics2/Models/Plane/fus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d513b191e25544c49b3e2e170e645f312d0cb58f GIT binary patch literal 121934 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 literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/fus1.jpg b/Graphics2/Models/Plane/fus1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a3cb7632de36e799c7d650080ae20c07c78e7d1f GIT binary patch literal 18136 zcmeI&dr%YC8UXOK*+6)QXb|LKL_ngTAOuAPL$%1ODpVjA1*>QU(So9YfC;EnK_rM3 zsuV$>pruMdQHmlULd5z2twxYn66B@9gjWNE>|O1h-sv4Xb9*0i2j}e0_s213*r zK5dqfzMR%Li5sC@N?#ej;-Kfr6kE1iJ%h03b?uZ{K$K?*~FaiAu^O6;(BL{DLxVKtNEG zKtz?4h(!GAgZTG=NKu+*yyzq4>0Vn%bM{be;!mDeFYL3qr_Y<9|p8NP3SL}b)~=)*@6 zzB+m=F)8iT*Qe7nzBzL?H!uIYg2D?Izc0OVwd~sU@}DYh*WRhC=iP0%_xQ;#PoMFh zH@66cqOOa^24`X1G7ImJNIe+eeayQB-Vtf)-YP{mU1(PKiyqt%mfL{>)A@ZuT7TnN; zbE=_ZaZb#(lf1#5>tsG`b#WeLU|~0H!VyF;ShH5>Mme*)v%<0mI$Ft-7dEw@=DFPMW-fD1x1gPU!HsgjV`kY_Kps9(a3XtbI#WsyGtiX zbrrgaE&H3wh&Qa2Y?M~}|MYNKd~XoCGl`xUM|`zLBkrwI1g+vmg65pBR_htjkhV!5h*oGK_)Bf}sv6Qft99f3BW?U|HpXf**G+ctKFSwkVDbcrUZd09A-QODfXiDk-}g1UExYfN41rl|iNkKuo9U_j z-d|PRojsm1jP$wpcAnn;xt9IX!fny~q!z`a+-D~GvRW^nR#!^hkKy8qLK@^1m8_U#OdGfh_ z#b4qL5Hr{1(#%<9qNAOQmi~ShQuWqF?;bVA8TCe9f1JF(*hr?%ugl_e&0Zcj@e4UF zpFCz*7BGoFv0si#gfyffTDp_ZDtKj(?nxyq7dZx@<+(|8d_RQ&#{e3D2A~0G02+V> zpaEzA8h{3%0cZdkfCiueXy8LO(COeKTI1p@J0-{>G@C3w%N-y5IlU{@I%Z#vmSRej zO#TO@otvRTU3rETDp?+hfnEGk)(QDa45W!V+2qhkjYd+bmopW7m=S)?@#(-Y4c*z2 zb^Lq(jkOhvx1?%0J!I;G-rmdM)qa)jm7TlI*WcP7Q*oZlNd zB93+mUd!WEPcG;5eo>i*k1YiQ$MLarWDeUp&1(3Qa>4sb2vh2_O`&z1F|+YsCu6vi zDy;2zGn||7wwcl)Rs$=joCd2#&Z|}o^sc@!U&N*w%HOp|F%ZPx{^vEjsE#{xn1U!7 z?c@s#tg?H3T9mC_)m1K|NyLg~{Ac$Mb-TX2M0j1EHW{O+%ccgV%1;cymEv@3V7gW%-0< zOe~*e5O3O}C%UwYn?%#z@OkX=@`i^trJMv1vp`1_D5TX*<8)<96|XRG0_D+W6pZCb z81!4o2FxN)d;+XUm;uOCsw=4KnK^?(t0H*5T#&-+Ek~t<$~hfv8-> zTa(%*sqAAJ_Dr#x z!fqi~66$+Z8j*2%=Q+zv&k2n~PNgO@?0Z037`{%cKGbyz^LlT&c1rEFa1P-dLIcnM qGyn}i1JD3801ZF`&;T?54L}3X05kv%Km*VKGyo0!pAC?(=Dz{TYfl;g literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/lwin.jpg b/Graphics2/Models/Plane/lwin.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bdb1945a3204c5dda5bf2b0efd65cb5bf2896795 GIT binary patch literal 104087 zcmeFa2|QKp`ZvC$+C{Z9CBjx@mWT{tQz6P6O4v>2BxD{osYD@!LdYz`PNp(!^PJ4K z%v0uJZ{xPd|MHyYbgJ|G&N=TH-t&LoH+|OUv-YyEu6teg^}Vj|^}X+?ebiClpoXft zDnLU+16%4r!;QIR@-tprK`;q1FO00HE0gKJE4c|Ncd@gLdbx-H<(d>FB{53J(H1XlQA7 z?4;ebYv)ezZg24Wz)pr;j7PsTvhyTMz zj{}|r28Bnwh>VJU`6?zUIVCkMJtOn&`}~5!qT&x9OR8&X>*^aCo0>bjx_f#teP8;= z#wRAfPEF6u&f!*8*VZ>S@mqv#yJ!H~KbiH-vY+f?0PWhbb0_Ui$hKWHJ6yqomSN|v zqvE?6m31LE9GH$t`0Zgn8}=rzYA??zJuJ&j#}2wfypm(baoeW-X4yYx*n@w{vTug{ z#jbu}A1w{|@@N?VIIu|&Nb;rob^IEGUq0~52Y&g$FCX~j1HXLWmk<2%fnPrG%Llf7 zKu|(Kg>M~J3zyqblabNy_S`&0N1;_e)}(V(CRV3Ndoxv+Y%R;H_b|S(J?Wh}E+OE? zXR;ZFLd$Lc^>Qjcc z=yRtyrNe~^&;G>MAXv1n1xuWjQq#$7@UxB0`;Mehgz!;=7t~1AWBM*{h=no_% zdp=2Kvp(ppsTXb9nK*8Gwnv;idl_kL(!~6R|GlJDHk+6ASf39CWlIHUo=dO1l_05M zYrUoRPm^E9Q2~tr!gFLqoy@b+vb%f=NQaH6#*kPs8_vMotEbYBO8`PGud_7IVcO{@kX&7I=I3FeYZ)s;~Vf(AGZgH zmsj|j+S0cHGUu?lRIya;Phg*%$sXp*BC4HtEaP=aLr=IM4$pU4`ecEjg(lj5g+;^E zUO!gRj@;${CG8g3Rxv2R(B8$Kt%mE)q zM>wk51GunE!}b<99C+>UR*uvq9nH{yMd{k;0J0p*KHB_eSQ^@deP(3k9lS@?BNTw(}c@%@2=%rjS06{!9V;Nz>rxNKO%<$ zNEhBRlFYhZgdIw7Y#fXt2UtfN-(~J*QaW@$`e4#BmMT|r@LHaFLq>apN z$BOYq3JdgzinDZxdNA@9PA#v)?1)&M_Cdw7g#|wI4De4qf@mJMD6N~(p>>Km ztb>cQu6!ZX=g(nqep=S+21*kQ6>a!^rC_MgoA-$OvTDB+=OMX(%sKbzM#qtc$5|CY z>adIDwcDYBYbvuf*ZLl8c5jZVLC)->0>*7gZ+c>fVjK3Jh3{h>)YxQH-+UP{ zrw;$rC0|bkEGb*e{X*^uPUc$liHnPIOFXm6E_buFySuDX9kYLr7D2ngDGvOj2g}Xo z{)?rJiBBxlIgUXc?+KN=CeRny7vlpK!})IJo038&?y{~mX99}obq~KfmbJjES7EO-wDcT3-z5bJhX13F@aw*R{B`}ku`=vF z9=>03{EEX=%AJ-39T^l|?6_E9-1WKhFqKD;HI&C|+JVOOH+sTf2Oh3`6&_@SA5&XH zNyB8T2Xh;`?k#ZzTT1B(_7B8L;9C%}WQ!~ifN3Ir!!*~+FB4AblsL?EtGM6I z9$p|cj?So!Js<|Ae2mh%8mO2WNU?aydt;UgcmW1$qUdqyQmbfHSM7CDZ=2S2W{zLGZ4*h7J{$xrAeXZ{UA@|KI1t z-;br#mMX#MQ%Sd(R-xyilZ@`nKKpEEi->7xN1T_BQINA_J-pMjLLN46=5n*Vwk>t+ zm2p|}(BaFvDsBw}yO?#A{`VZ0pR0C$%J7Zm-9`Ixk@p9X`+9S*z<&Ws|N z>L7u{9lwtM{bPVe;sc?P6Pjv6&nZ^$YP2tk9-j`29^bUznx&Iog>`;#+U22EUh}@_ z#85}At_sUog2(qh`=2~#=_AQ@R20f<(w9sfu^*}EzL&f@jm?$c_gukt(C4rf6}XOF z9x!zfov@$IP_7Q0LbA_Sbxpr53q5kC&U2seMRzM#;JRLiis$t78x>|9nqS9%XAI0v z;qHo-kjp-dE#_^(seoB|Oj+{8J~+B_lJ;%$UV@!eQ?OA|8-gb?@7-3skqWbsug{bJ z(X;d*hP*rW>idUu7u3dhf-&#HjepaN-TgQ)ttrcpG+7VJUxbfYsV*hl^+`lFByKnokfsMI;wv0X*dB-iLm(QezZ8c>A zZ0Mlb9q3j;^I5NP-Da*2)}_{-6n|}7 z%bpkRvTdBLYKdlmOohJgv%3ks6{O)5*VxbX#lK@YD3X^c6~HJ{fyAIf z13UP{_%fvgO5SPkwii{m$$>}sPf>xBAOp{n^6y~Zb@p-}NGp#W(|RU)|5c$hBK~Vp zJ+jXXt{<}^s`&Zh{EXcjws0c`RCkud%i+0>pph457n>8;mJMGyD+a9ve?c7)Q zhtgiHti{1c;gJ$4ks6JluPMBh+X}qvlLc{01c{MDe}LjUg!+*UbtB~aFv6ZJ1H&$g z;t47+pGXDTRg80NNC=0vM|eg1RWNS8mjlD`znBvX>fF5!)mp0^ifmKD8zmoUbtr_G zG+2FvEf>?SwN0D#n4oINhiAE1{Xdy#I}m3T0%zbfL33F%_X^0bB|}^DO|*N(^`cdh zz~?D}%KSTK%CFz?&-c0n-y^f^-=ynF55YBdhrV>aKzgk)4%?dMK~2_Y_O=DxT=BY? zDVExu5amR+Wk*{i74*aPs#{{7nGb=H^6Kk9Lc3!*=|6@_eBrdhvA>DLcPyEGu5RsaO*QPgu!~6NOvr@_cH|Ll{*zUvy1UkYYZz zivtkXN(v${t={{URsQ$0>J$Y*t3+sgnKv|ILR4K%`=s<#BIcB7dhJYE#bJ{;WcG!T zH6DTyD?;{(VHVHYV3A3mhJ6$0iB~wQ{-9|2E71N=dSXmJ8~=w3<9eSzu_!Ioj`|d? z#rvi4g}&*(!UuOcyfb9_BWcx47Tcvt>BNaRfhD-|RB_r;5d0NMcu0+9;pK=d$58(YdA_^KVIZe`#R;WetqH(fFq`UKg1YhKuyK#9_TPnI+~IJU)v^3bJTVU+V6BweWS}PK>Lp6xVNZ`fU*iK z`lhg4kGbwT$PXC2{OfY=e-~l;>+gU8?;APIbqn`EY|Zra9q-;Sq!gI>8HE`YdR={@ zK1MX6EH;O>UNH*7JPI{@>%9rpz)O~%27S|kYvvDq6O@*G=bMQCm~XDb9+rrEX5|>F zY~4igoeCY7FJ5Dr5*Ks_X!*M^{pet9d>A=!tn{J;5T<`WpeK~C6wCbat&?Ivo5KO& zFJt8JVMJIl$ZHjO3H*T}|0a0?8<;AeQjYOApVWE%Mhv{k(OnM6pM^`zI{y`4#M;YoLTuZ#|D;KlV*$n9ToHPFP zddtg1MXv;v8MwTU!Sd*J8WXVn0$cFlDDltbTBKYG9W{fjOO0SE{L`D%-4o(1-B9*H zF8y-g#5A>h&aA8cZF+t`Rll8|um(M-7Wp}*lL+zW_nIO z6`0{e+dBlnUwbROJvTk;s*CjDLwsW23ev7HR>}<(W<#|L|7@@_XgyJHs;2v(lY!V2r&0Fx$70~_0+?dnWc+54a0Cugi`?gS@8-OIag5Hr_JKC03l#<;;!2$6EaRJ0 zpuo_dBR9&}C$UPICgsr{(YBOwIfFCy{ikcDh=#FFn6E2|W(m&kyYHH{Bau!*J{~aUW-P*h<0VDL^KZ+ zJJE!D)%dk~+I=TzG2_W4c~Tm12hvl?{;qvXsl`K+(`Ptxk-boDWhJg56*nkJ(w=+{ zxdNdAcF7cWY08_5Em|s2)DEf8H3LufAE5#|BP2g6FaS2p9tTr_4109yg3 z)Vge(HvO~gFd6e33u?PlLDUg`h7X%az$>lUUZes%pI4XUr1 z#cOa-4z%hyoM;DluCp|H$>LBXejlz6J@TRa&84>9rKhL& z1Sgb@46owQtnf`Qqm%!C)&I+?+?4tO=Jl9_%cAp)z&2dMtEkve9$& zej?QJFBLWp!9a4QRN?rNQ^@|vz9lZsVJ)YE3Diq?B;aWv>P7_~_()QL>TBafPUwOe zqTj16p}GEaeH+G|vYhG({EH;kze!Yw1g{Tb3C$~mpoSX>zpw(rTRVA{$Uh39&jKu9%k5d5$cUb}` zTowFHxH@m%T^y?j4|FhcNG!inpsV5$(7bDR(78WhFV9I8kp^E0YuhR9U^t^iuT*9k zJhi1*E8imIRsrnvB^LX##9WD%s;YUVBS;2Cxj_{dg8jqPm1km)(fvmpIE`Au6%{|C z_IEoAmm9AyitFmN(hez5v9lOrjv89Ey^GGMWU*APn6wGPoyjY&TGF5 z1X*yfkc!DSZqbzLVgx}dYq1$0+JIQmDFY+cjcX_}dpAv6$6wMEnk6^{EuwBN(=M;< z6sz!XKr^3g*sK$*n7rikq6}MG)9m;rN@zkVy*ta{a=_Aj zJ<=Mw#Z-_$+=E<{Mp>ZP$SLqitxn{^Q4pDieHE=y`96n`O((INWDn?+?Sv3$fh8#d z3PoZPDHgtRe_kOjpS}iNxD>&!U6#FPeo3eh^n|no_e05P{Ub_FET04=xP0aTHN-7L;b!zs ze5Mo%!WYsY&3?%y#M>ZVv)p%7fK$^W=Qs3X!5P)sZwV^$P=R+3M+gk4LfdiF6Y!bz zywt#F+*`56qjUte6MT%^8RQ0z3MBEOP>;O)vHBE^r2r~m41xi76OL=kLh zA0&d^n7^V+8GdZOye+E0;Xo0jDTMMssK?f5oEYLIR<(+J3Ce!LI>9phyTs%lJio=i zBPjm!@Wrf*vA~NL78oE|On#cerA)E{bxrYw*jTldJ9nMcnO=oPGEf0?kFa%kbGcB4 zRmpu1tx(%rT&HUen@nUj@ULdAuf4D)$_OO9yyXC8Ff)MZfPG8-7lMM;zZpdvgpZtf zirDRD^R+gUoGLFOdQ3#(+EJ(As&~?sJnfi<2u;yXE86lXkxp^fu)h3LT4Mtm zF6R-b;t?A&xze!q!;*OAkuys=t)V43g?_U2YN_7vG2{*_E~a-O{q^;%VXEv?R>`N} zJrAC>Lq3%lRZ#)fxv&5tt?_M3)_W~%Gw?peiZ@xV$er>C>$~PFi&G8Qv+wg^H7%$d zfkfzI=g@t&9`wlCHjVN^EpgtWr6(h2D=psJE|nwBcCOE>7IiO(=01N0m1A*4Pu!D{ zT7}m^A3EYsGk32gD}0VpXJkD-%oFioKu0L|q}+NURCMQqJu*CRP}!6N_$fFG-b|Qn z(xrQCM)w5U0H1OcU5&_xJKuI)ADZ4UGZ|#gpBx8FV*Q6&6J4w%tqd>voEBO@s!^TfRlwxnYdEUGH99 z=DjW3*7V`gk|)j8(?t=(8XW`mopSXn&);gXFz0hA0DMWj;p)5*gP1I@ec0lSktbrg zcW(|^h};$3WNG_6b~!{D=5me-%q2nvP#G1LIogW}(CIvjmaArRe(_H#PcF}0>)OJ6 zGCI9tYS_s1M-J@vx7qMi5m-IM0An}|>U#968X=>QS= z_u%z+q2hJYthC%lzHRE41)@p|xFon)q0?ei;7BeKjOZ_@02tEy zD5~hzMCgl(v2*we;s!jff^;|Z`+HGYU>_k|%!kem-K;=4uaDL0V>FZp7ugw^@V!Yx z%u1^c=)V6@5a;xD@O(${WF>Ia&WFAsLtzLJjb=xxr%NG+mPscjO{VRUY*1R@sV{ z_lw(2o4J)Yj7~>AlH@ehgx0eAWF2trH{q?^w_5N(O}E27_as<1fR3?^XvlvC(X@Xd+TTQ8v!giUV`$+6@Sve1>o?3s%oK1Q$70G~48`w8 zH2F~q8$v*7pykoXyu8(qQ{WVJWTKEwas@mTo6^Y~5PT{ls#@Bib9vq)_L{AsjSp%=w5}*9^CX z-zHV`^g4BDt`e3qxt=E4u%}cwb`DJ`5|tGLwhSM+2Q<7!5YLQdZ)Es*&Ob>cNtl4S zTbkRV5=?k-P~Fv8>QigU)z>Lb&u&LVbVhydv?UIBo^c+LUlGyGRcaBhWRrK6+V9!0 zfE&A#tCc){CS~Yr$-uQ0)x!rD+~7;juwGvUWMw>K5!U{Z-Te=$7X4gfTU%d2oX}E` zkr^*B{=KBimsfeykB?9)?-bj8|^y!?Etz7S~B*3&a)<^o8s`F+Esp(JNOwKXB*QT@x zahs*bJ-X{_6xa?K(Jg6VZwwHsJgJ!2l@){c^yyEiY27jV*)%)ZP%%5%T0ucMH|j_3 zc9jYQ8%J-rR=4U1)(jn9#87tDC{&cNm+vS$wGr0CY(+TnBAxGI^y`MSS^x~CY4HU# ztA*a`MEIC0! zUFqbAak(Lyy03ALOmmlCtgq>sT@L9eDT$Ma^Iukid3RjCZ>sEUN3Y#&lsZ)46pCl` zpHc7@TK`88=qGGJJwt=lgf>`Bc!y5fF7$s}P>nAh*Lo2nR_pCjFJ2_|QG)yPykn6ZOdd=82d zYzz<`%PcpZQ-QEE>fp5iN|(om4KAVo9Oe8XKcyN*)SRK6ius|EK}-Bl=vdLBeosVc zo8do|F@)%!OPxe$dWEIf7Jo4n*kFmE0xBwIIbe;$O-vw6jx=IkP(CA8wC$^&ET7Xx=M730K{fx zppCwGm4dTD0=Zq)zqOY%j5Pq^sfvD> zS(H7~fR**p)7XK!Q)yZ|Uquu-Y+i9`^%g{Md0j^nk#(95HRiX%j&iK^1*Na=_LZ?A zvW{^*k%T{vIj?#$!lYp%)2)h6`=zB{qiJ%2#@6o7UzY827C~a+s{B)S@|97f91-2w zFDxu0dh~G6o?c0gdx_WY6LfZVe^>m0@Ki3g2r|ojA1@Cb5B}uzVE2Wc3koG(TMBYU zt;W)ioLhayWeOH0mTvHq3a@a6sbvZs*yWv{Ia^dx|61zN!bQ=7N{FgX<+JHGJ6+PO z38!DBa+|`n3JPezAe~&9hnuZrrU}GOpY4{H8r3{?p@;YJ#aL&rf=jh_mKL;Az~pY) zbelP=pLWpO;-4@q<;%e_=@hKe4_s;bdS3drV4olHxuUs(_%gz{G~4R)0?Xywc`|?p zrrKjTvA?K1!$w41+DJmPjd)2rbA3H=&sr91_qwO3;>kfsYe`LHec7XhJ+m;}OiwdCfNWtE*2sSSfkTpseTP6@; zjhHV5zxSanYzgFm#|42>3L3hcB@(D%ohuQ(yT@r12y4526%Ft`Zp8^Kg~2%=Z>%I8 zmaSsQRhy2f#xhDKFvv@?X7q@Lj`HeXjTufqQJ6b27r}VD^n-hdQ~eY_M?~dpZwd0@ zX^YMA_(z9LtN5%#+3IU8+NO2^-oa+4h!>EJuTwx;3#{#|%RQys+7>~w?3fD}_-{bT zX=^mAn%=S4e2RKNK*fAJ@)Uy2=t~7XeDbB6C#4*+DoKO+oxr2{3M=3zMS%T|g`=#L z2kR?6-WQ2ykxi#VDb)Hax+hjNm8x|8~V!W&}1PI@ZQ9N4WPaWv^o^ zw8by)T{8r@mo4X@i)S@a1H+y@<@4i`;|YhJrj7{vkzrxQC_Uk*G; z(pGA#5;p2#{=Vr!GxSq^*yclSMak1`4OUi^)G(z zH+(<_IoM<0qTxM*stv0R>$&3}wV!mj$M307z})h=BrG(yHt9Tiyd^M=)Uirto<3sg zJF}O$e=B)yeOqU>?Ed}^%nj`P1xh#W{J!~;?R9l&YV?Fvyy1()X(^^yR#Tm1q0l6Sh#GdC z4le~ub6<8Da5=tQ#Ky|cfY?^$H1z%9rE_Pl$L-^)MuRJr9uCxutR18RjS@(l6pn?NW@DMeHw;2RVgN-M46K34 z6efEpvKv2HwOgFg=)B;vEyCqO2o07Pn}Kq4_~_$eVz(9$(l{)an8^VR9GgkG#r08eCWlL zRsXe&&?GN^PVw%%NlXc*4otkH^2VsZlNiy})5lIdF_)PMU14fp8;VyDUZndK@P7`} z|E|yPysTMYkCJuavqPbir^vz{`RjdNWI6)3gc$_Us(Gnryd!6?W6!g)j@47-)%a=2 zey6x%aAC}8^o=E-OxE9)!!U#4`}-!!e{w0zE?Qoi;qr|;Vd;OxPjGw5apUeVOAHNUNSrpaLZ+y<4@ll8*U7`zMlpd{D>SWoB;}ecz zNGzFann?;;ZwvV*c>Ap?8NJGx680Ls%BbCoCZPP-t{%?XahT(v>YhZXCZ6%F56_oy z8Xlhmaz0bWgc7tap0Qbx_i83LQw6O8(n-|Y^iMEmP~M$=zp?ikalrOM-=bl>!84|r zy$UuxZqtjblJD?v4XYHlriv<|T4alKA{1=cKFqI4SUYKrz_Y>dU>|g^WdMBUdS?Yz z0**sHp^Si=Q^B36z(|eSz%qP#2}#6T(Jr>(FUreOfpfbfseoS?NFBkjQEpC@$NPn; z04)y{V8@3pZYHd5!pS5_Z}cj~XPOFBJoZ(be&2h)T)p{^7k2g@>idX{s!TBn;!0h|# zSR3K6B#N{zlmzCc-y6}syN(5<4Zb%doj=G+f#)q>d1`)MrLH?0FX4a{t#QzK=P;HI ze;&Z##U#U=pcQQu+*(2Zpp~&k@v!xY+a`n?m)AFaMtadcxl~{R3UUr(M{$dvF+-do zM5(IAGsk_@_;c0LJ3opY=&$h{H$Blibs_nI?T2uqNM9Ab8s3P?!)+kV1Fz~wY>4?w z+R5Z%it__GZow2gO5ZKV_Y_~D0@z6Woqf=Qe>nV!`-8STHy(FVnkMEw*G-Nu#)!+z z&1l3A$9f|py_tSA5p0UTIdMN2q1J)APu8fP^K>8PzvkXCV4Lfx)Kn=*AhtvNgjw!n za)*qgC$U9oQmDPNa#4%*Q=Ni`r{Utd@zZFKdNo)_RSszr)yABkscb65y&ou+<=Ow? z06IiLfVKm4S}-L3oyv$dAt|M}IDYB=gMK!?SDzVWy7R@?07qMW3IiNWmD?-cQh`y9 zgfXSVYla`O=}#I=X$>04?oAYyXc38l<_^sx4aednz*J`7!`AHp|cG5JQ%D4nnserxy!2>t!`N-#;x_mz?qF7k&$N{Q3^_l%KC_s2TP zsJr5)N(%5+_!|IOmj1Vd2!I!>8Hxx(c$0brrD_F~Gpd{E{(MCVriW=ADS$m<((Vy0 zQ`rF&ue{e%8a;WQzTLIIA>3&T(G7!V@ht3PYm&1KXY2~M)#U5r>9Yk)EZbLZlUO@ zz|OYRivp*tH+Kp}O}8Llx~@a}4k#AT%oqOZ`~1M3{mov^PfXk2yjS=0o5e}}_Dq)J z+xy<=dG5Dri&|o8d4W2+7Z%1txNLwle%jQFx>>(N`&cM(riX6pRhD}4x-sKaI_o5e zqskfEeU$HxJwHgT4Sp1Z{cB5A(zypGo`s~vFu52h@Qn~wF=C&uPa3)$6@=zAB|ja92UsX5jxfTKPmjzaY}$Vt{QsF$0a5VJq{^S?{t!5^ z8*4y*-7FkA<6vZqo13#m?5)P4d#B@QNe&(umLG4pK~zRIzJZd!*`unW1d{<3sB`dD z92E!=!|RfZGpImY14X7{?CP4%?tqQqWDu2M_;HY^MRZ8LRxTvVYSt^4Jb{HF%M`M9 z6iA9~9lqa2AU*{#8Z1+mp41iWmks6Zuhk~62o0_tFD%P%bl|Wu4Nauw%bY>1h*>AB z&$3c{g(2gQQzTK3J;w z(@LxK1*C>a^&W2ucbZhh2GTi9AMnC^l7IUX}r9kh5 za*bdWheNmFvY*#g=?5g;11LK8ARf2LP;PreP6_1(Zm!oEw2MJPN6uu}XXh-74tO!u zzpwA(0qdX9)%Nag;3uFmYc*3Ks;NSK*S>h>raj%NCv1HNb}6%F7Yb5oLh&#?c#v;`E*JpxDfd%+y&iB|3`iW~!(bA)Failw~p zPPED2=`BV1eC~Kj;WRBm_S!f~cWpX^&YGozbL02k({C&w704eUy?6y*fQeBaMt)_P z`{BY1nsrfSZ)+Cvqt>_@ag*~MGSiXQm1oy8XA+_Qg_??{!iHsC>7d-o_DnTdF6*EN z)2!)c6`ZN%joTbXd+4dDp7~=lhzLuy3;(jrY^X={MDBxrMD4ZMTErltj2&@Ox6v&P}17YSez(|6XMya&@qIGxIs z_omP09*%RUL~EFnlKtNw*3Ll&30rePyKvK;#08Gsy49ppT)7y}&cx z&Dx3$x)LhTp^0G7ffW0IE##9R6NE`gAaEF9DD8+9fpehj9K0dS=;)7t*KYhcB5e-) z#936acjIWsi5pj0^JXdu&}LsnXcBq|Ea_OQVQWG&NW2(`Vj#745U~f@p7jQ-9?)VG zP)2YpXFokfmGcw@4Z>i1oj2GZ-Xr!9K4Sp3ez6EvD!`-_TE)c_q!JFNpUIdxCoK9< z>4Drqm-byl^c0oFQ72$Ck?rSf<0yZ@*Au9-3Qwgt#*T4wD$aD8MY?71tCSjPG~N)<%qvDp3erkyA5 zw<`8G_fLI!-e5;p|ES}=7Bfwp3@NK&hITn)$UOMA>b`7RK?t|MR<7mPR3Kakps8iK)s}rm*gbrKkmM+F|tT#2sa!8$rPdV?(hA0yFCy zX1_MWb3DGfBOCg>MjkVwp-Z`AK8mTx4I5xf3tf$uJ=TJaRW_gDi9`yTz`i25Fx^PN zS)~-K-c|c3INIQ|%W}BkZ2UG68}nxrb=nAd0j0HmzGR=``As94{GZO@z8?0iRh0ZJiTzT-{Hz!`9v%cwdFGQHWQ zMFMBr*)rsa)C}nH-&E@VXmQw~!b}79*tqT+byGN6T@Si=?w$A`RQ6SMQvTLEuZ>xR zAZDolbL^q~B!qQ@@2yh5u{(bCqpI$ZbhAio`A|WTQAEF1s>K7<5EbqRY+8G!DB^Bqk53>*J9edc1tX~D(nmTD-*XM;%%Yq`hDS;q&jITPI-Hh+CR!Cb@S!PR5RV)**)b+L*W6u#<0=setCG;qtQ?^TxN0djpWEtzZg2 zGZFLSV;K<;aBDrLpe za1Oe`g$dJE9P1Nlw(q}T|2`)26#EZo1E2rNnm|%Kh>Ik#O$01~>KSrmEh>zLrpf{U1w1Dlr4jE2v6N>BSMo@X64q z`rfM*Ob(8A5!?aK$G)s}jMwK`g=TbY_H*^s)dLg2VoIh{&2BGNF>Jc@N z;2Bw-6IcPbAx(Anqy?%Qc29;LE7S8dOr|Vz00K zylPMGeFfwF8m&cx=SrXu}tPH)|^BulCxeB(U{e-qr zi+pe~*5}?svJu*mi&qGKt711uA`qV!G_z8bB1er?#w zqJ=r!=%#pA2V~kz%v8G;d5}M_44GwfuV~&Sy5->n+j$!0N!rh+8r!5=bRd0TzO#sK z!e%Lj9{#kSpS-a!LX>g_iJ5QErH8Ex?2;Euxx(OYZ@%xmWl7@hTrj0i7C3O zyhg?N$mEIHi4q*8+<{?;qxiiXhx5)!BUvpAOS?~wR&7 z_5itJbRSN11Cu01BTV&=n%m#ggCAnm|AW`$f8V~AvsVYXKdmgcJYbx3wd?*?-lqBO z-?WHs>uxe%Z9b79+>uyavx@B{RP~<9Kk=jg5hbGFWpiJJGh{(w+v*ba-ZEut%Llq6 z637p-_E?h57^|Q_GW)uL&|SwFPqCU-9->%v@4|eMZNi8;ehn5SoSjzxK+-_hcQ=r3 z)Ol8jL09eT`dmrRaXr&3pa~W~Td)J3-&x4#12+d(a$o!Igoj>6o}-Hn&e!(je0{0fvH1Gp3Ftd+O5xh(1Bxi#v+gv{ zqV)6G&5Vk&khv@42Rs*cH625rOK%W){=qHfwnN8@(RRpihbA*F@MswCxsEVD2#%S6 zPu9l!jps2PRdJZRj2f6lcXf?3?VLBpP-T13wkp7ZKJn64IAKb-f z`yi{lZ#3bh0Pj8V_)4RT-x5j=>H7W3lFN@uy7ElBREuVyjtdYD!fgr*?yI*TQOzb^ z?a}>m@hiJaN+rVLHR{tx(o9?y5mMm)H4V!*iyxCBU+{nTGN*fk_pZg_QaKTWHiBTE z)>L3a@O14g1IMu!8+KlQfa8bw%`UI!uKXod;GOFFJ!j3td+%_ZzFr==dnx6vi~>a0 zAK`#$s^BcBSXd6+bP`&;*mvv$$92miQ2WdL6~+Lv(oR{#hku~@t5R7})F9Km??eUh zFv)g~fJauvq;H4?-#1lr>3Ohn+9}Q}*7xp2p43L&qd||yuWdaUKDi!D$xypj$v~Rf zQRk&LfdZB5$H7WB2SW+AAv&UYa2V1HF~)7t$2`bb7XpZF1<7$Zs36&ZhoSOqC&WOB zi9Shz3LtK6u@g`K$r@0Za_WL-1)X)w+q>`^9|cD5({ppgE9k1}Vus6O-j}JpF~&!q z`_PGEmORD6&&|Pdy(WXFI2G^`H=8C(+cMN;*&3f2Rj3toU|a9g2*Z%ydALZ8M{hXJ z_6N5bN?tDSQ~kb*ttrk2 zo~0-6j4Zq`Km%hgo{sml@;i zuq(sYl=8CQvjp+eR|5Ir*JwPQTRnHYGvAj2u8H!mV!=u}%1elZmGWdZzmGfU9qb%~ ztQ}#sZVoExS(x+n+ z@x{p~Nmad+9ih)jn}<=X-Dn^!Ub(NPZrPuQ6~JjO-;{YOfgM#0O+wNxJj!ljXx(w6{n#95jC6GHVSslNq!X zKLM~exWfLYYVj{A@b`4v|KsNepyLJCNK&*DOQuIjRp6#gXUkW|FQ3)kWm>$+etLUL z8#kW)*~$wJW+$#!xOunjZ^m1c)q<_JHpM5PXz}|c6+cTaf7mzqpO#*N8gtXbBCf{S zPn#4ksr5O68qu$22KG#?a=h2`!*}=%M)(mhoj$P6F4gu$5q((6v)w$U&it#b9&Fwx ztXCBST5(*$U)nE6t&oD{z}=9O$9Md}E8i)(y^hR`6BjzB(3|fye60QG>IqZzvH-Uu zMVaKO>3ed(X}@K&32?R6o@Rrfum=bBANSV89U1M34qA#e=sHg1N?9y%DrlN^q43jv z*x<~XMl!jbF~$augk|-+?<_jF^<*I6)=FLjGmZam*^9u==Ma6<<%d3uZcpo^>5RqR zU%{d0?>pKxHf~8ja7#QOrqz{OXs5bgv0m)-+*q|5*Jx>InmXVkwGOX=KROgo|0Qz> zo{%MZ?c#M*WZaSoL}*Cq^BGUfxlxdN?1%Yt>!%M;fz+29nhxbu!2PpRK^`qO_w8Sx z1xz;;ZOO@nV(>Z-!!KKj;6 z55uef;_;2U$z3fv0q>UfpTnXw*-^ENJ9bDgPMW}o7W?9pi3Kzp$0Offy>hKzZIe6j zCL-w>^s9cyU-Vimt8I(?X%D*#ICa$|YL1sCXr@SjNxfaEQG3rji7IP@lkgI@Zr8&=VpA7fpwo<)NY!UI;c-QB}I=k~bCphXa`S=qHalziQM7cTD z>B`NbeU81+DWT@hJ6+cID{)mJ81VBH4OWoC76z%yWMlN;Uy+zftdt*?ec@ZcCC8D_ zZ!3=f>@(0%inx{kd3@~KV<(Q4-f)XsA8B!g*OijQgU0?p_P#r=sdQUAhyntF^db-x zkPgz376j=c0#c+Gl`bM3fv8B8A|NOo=^g1El#U3YBOLc&pr3f zojEh#9sl5$M)ux$_xrAQt!J(EJZUkrahF3kJU$C1TAhBx4FoPWj1;bwFN)H3LPs7Y z?&B}r#KQXA7xs!%tZ~=30_aB%4qL*gH^T2JpC)%gYoY_+t%YJmM)mxCd&XgLS!)t3 zDf@z%LzY8V#;2ObOfYLx*0`?#Nm91$6u$?1ZQvEP;KGQE7k#d{FO?T&t)%nkIcKBB z;f9{C?MtpI87-Y=AZzp4HZk26o8wJ+;jjqkJnXn8O^lTq!e-Ak3Hh7&CAtB<)XVv( z$`?7-dJ#6chDQ|XRClKfp9v)2j-1pX1eSM2Ihc4Ad%0G0Tp@d$?er1E)yjAj*vTWS2K9f)5w7rUC>7+_I@>cMl}(6dP04#ld4Yv8R5DM>J%&#d#T z(V_4f7?4#Y;YAZ=3YS(T}!PYV;510!-Muhr5d-XqnHctu~ zaCV^^jA@vZRs-E~zb|!F)?mzX@3pn%S(lA16L!a{EP-tgK zwdN2>b`#Zh1IQhyWdV|TKjGEL|7A=!^wYA5@6UfIBf;rKuJTKM@QO!WH{4kQIR8kT zS-@I$D;nf&OCEMw5k+e@_*eJtcpxBt~KTqpm)7DN%pC&| zK>qH|oh?4r>PSoA0+U{x7orpI+80@MxrHBR4aW5rn(Og`>6bSSPs0Z^1V!SlKT>R- zHp5n|l>ei3gVux(krWsvOx?dv_rRjchFWf9J@3dQ@g2)vF)+yj9w7oTrz>E`0A~nL zo?2{@_Cy?l=grpVeXdX?Z{@YutFroYIEHa7_r_JD9_EnvYEIXE{2?JrNZ%ffSCL&2 z)7=?a(k1YcboHEPA#8nrPBK3*ZlBRwDDh>PLIj4!q)Y+iDP6pNI47z0Dp^|IfT!lY zO&(F!MTnNU=Glr?X)W6%K;CTCvVV@|BDFV{TDrfjfne9QFCfouXQJ+05^fk!Nq@tQ2m72^jE2faI47;g7rpt?W3>8%VvbPGM zuNBBmj8;qe&qr`c0n7>|=ywqM8B&e$+seDUQX0dEffSR#{0Z6Ek^UC)lSWK@vomT6 zRC{*vySkg3&46e8OJoY?Z@=V}rkVnXjj}qO67)(74wNYTJJ3qNfS&qKy!G$zaipGh zto4oAb^k%AfltC%<;V&$(ikbtz0}0ivViQ?GICnPLl4pK?ou6>!_wz+4Jp_boP`12 zkL<}`W;a8AMp*T;4!U1cB!jb^Yeh?UP@FEsl5vir#Vyv~iEnBrMOP{XQA~%R4#O0~ zAK!NUqzRVMNZSJkxA~mH15vGg87fE|+pbp$SxmI%i!ZSTc<^Dd|C3optOe|?XBBvV zmkFBL24ds=dH?(0SH}0pzm1Ct*|r3Pzg>(S>&5DTBExV`t+n~`v{bd0s9@IaL88ep zb}yKUl@G;zjjCJgvOM_a98<&s0S2SUc)&uD`$NvWurvO7c)bJK=Wh;c6jfvst7S_M@aTqi2?dR=J_br(bMoshajDDk zX}n#VAuKnyOY6>+Kp#3hhphAC?p_sH7SX#|`r_tb;l@SUa7u#o&x94GDyj@GH6d5p zMqSrb(^mm2TS#J-_UkuO9DyGu`M23l3%HoCDJ`D-gV6WKTCz3}r4~H&*5535`G59A z3;ydb+wH5+Q`xA|BE2_gvk1{z`-Yed(;g^lxH}Z!Skh*D z%_j;TmG1=drboMbztoo@)BS1b#GQM(?O*WmSyW zpu(*e{Kl_s(u&pa5!>e45@v zC3f3D@9;By@MopL$1E+>t82kan7t<54J0w)=KF!AT;C4A6nI1Ug-AzUl4O9g@gb7= zf3ep7a+Caz9;;#*WCdy5KQt^-N9ndD_=TFufkt7e%&?~Ib*Mqd`C?V22gZCxI%10+ z(v+H}qQ3FJFy_BjJm4A#$N~v#s!N`2lm1w`GUDuW3?zG=pqoCXVS5BZ4X0q#5~tj8 zkUA)C1p~WW7!NOdVS(mEu0o>9>3PiC@|5n7GkYu8#^!s6p@CQ(uMpp1NC+#&Kofa+ z6#nDJRQ*s7oMfOveyxU4-z&F=lwMij5_g0mU0y`SSlt8tPmY)c3jquVW^mIFlO>%p zhrKt>6u`y6;NeQ&@?J~EPUR2n!I+R5 z`jYmp$N0|g!@iI;VRk-4G9S}&ne-;tmBEGTO-BSo!#kIAGENHyXc0&~lN=Z|pZ}#Z z`Tg}jkcmItSU5w@10o0%xqK4LjsPiO%%JYyR1vZyCV-~i4s`_`4`IiE*SywhWlB9e zCu$v8gNf#_CH3OC$0@l=cY$Sg>Df<0vfqp={Pw|16=zol0ZtpnXQ}%Z54i|8ed(;T zHMi*}JL3ZSy$4gam?)t_2K`y9>em-KEU1~D=g9;eFp{cADEB+qvmC!`r;*97L9a?{ zw9u1Oq-;pjH?K#A4ayeGBnug&XHxA2-_b|6YdwmNo=UuiG<<8wIL^CRoF-<9d_B{d zr%_vCj?%VtE_k;-1sw<#T!8 z5TZOo43&6<)4B2NyKoHCX7`uwBTAm=yA;HKKgMyQig$z_2k(GFm zf6qNK@tQ;p`e~1io=C2@G$On}%^2z1;dxb19{lPENYz|{8hiq}yIc;WmQA=v_j;P# zF4r92V8P-rLwcV`hHwWS^S35l0kdoMRaAFCR<1}F6s{AJH)ZmpFOMH2%Z{MW<+VbV ziV`lF@Q)H+!$qW$OouGkW7$3AHXSeSyOgE84MB-knfV+(ff%f8IPyBar7$pRaUxqr zRJ%fgQqL~`Jg@iD9N+infAH9FCFjzlWKPLrka17}J>_v5?oh#TjpdFZy4SrP7fFxlyg&8>(=& z>e}Yx`Y243&kAIgM`UDwa+%*|T(PIQRgRo_L;15s7Bqz-gYR`cepf_OvrzZa#7dw> z^=g^Tu5=yeJ`Mpm(+%h?0J^XUOoPgIZoZgrP}jqH+CI#jMLiHVKN|1>XdL4rAS!+P zIUV3-cYcS9hnHma0M?-p5CDnKS-xNr*&?<>-2(gGuh^Y4MKS=eJlDve)Z;mI(^K81 z@2uEQU1j^*&gh~OlZ&*IACZUJ*iehz-M2~Ab%fESo|BaaT|3S6Tx*mZpdfSGXi};L zYDo77w~}yB`PAS{_5=Lzy=j*jnh9HiCbx6CtuBXAf@e|iCz4*=fk5v|V26Pt{cFF} zCWYg{6|s*{js=`1107`c9P+~b634vd2+WjX^L6myz_X@8klUW_gE%-N&7E5=Jq90l zS;U{YK9Wgg2pRYeoc&~%I`v0Bk35p!%gm5I2l5_-b9kqmw>>-d;I1k9r0(tBsQkP~ zeG1L03Q&#S?Q2HAl)sqIecKwNbcWAf3LFKj*;0w$VY`)~6jaU2ua17dFYPZ}w0o?+Hag@H=OPGZZ z1zSix2g_c6c+z9_j>(`&Od1v7eJhjh7LA91;4}Xrp=ejYM;Sb`sZFsU@czMT~I^b5Q}|h)asSdA86V+%Igl87%_^37e4&QGE3zs z@4^S&d!J=7fHY!Sbp%olEuZa&c`=`)PE+pnO2thkA&3}%UhM!DG;Yr18fF;hx7omM08z?i!u-zW?E7$) z>o+xyi~Ufy@KqEpmDUF65@*~!|L7d=kCpU*8}|MAzi|va*jwM!H_^?2w~p)~TZvHB z`L%jvA(<&vV8?WoB1T^i-&8W>M47fA&*-y+%tW5k; z+HRtedU>(q+T$Y{auZQKX*qr(jK;jNhlKaOUgZ3eN3prT+tjkVnVAL9cRB-+`j<=g zpXGSEhtOhacB+cm8T!+AErs3}DfLB08oauNe}0BY3W6)Nyt`V<8kjrixDX0Po@Ij) zYXTcGz-LfC$#~*iKjT++H6$CHR_=sky6xS<2MYDh;)lBoC}z$P@TZ$L$i#S&_(pc( zg0rl_l;uQ^X!NUwUG$`Iaz1xHNjZE)h8lHH{z#|#?le>CVJdNtSZn;{vWWF5llJw< zq<6gqGcT_esxIzBKptv(Y{gO#vtG|#mLQ<~_izBn9KZewBAnC@U6$b(-D_xibkTLG zxe0L=2kU|}scEG28Ov%Dc-}UisRGEvrqsDtzQS^-VqZaKwoL`sny7*UqQUW zV_FEX_bCE&4sKOi4UtALmaIC?EaCR?xev1~P>PAt6geXt)ToAyk5!KR(jDV#&SY5e zyCh9-Qi;B=-G7hm@;Gc;uCzVSw%mH(eRKsfQa{pQHBT%X#&-h>(_u#da+J|}Ps0G| z-o719(=*_d6HdrSOz%&aCI0@`8m5`_%B-f_$7WAcz4DMe!~nn&Ie*DH`D0MjkfE=V zyXtn}gxhFE7Gb{+dD7#E%VnhD7SmY_9zi)Aq*Z;P~@p~d#nrXD@ z<_B~?&hEuF>s2wMo;)WVs0D~SPJB^z(?OsHifF#U;0okRawdcOi@k18%WkSMT6qBI zK3$1ts-j(Xc)&FBZIg>Uaiih*t(JE|qxHPSHXg@2m;{(Vbk->vov)$34IFUavU1S0 zgg?>qQP&p;W(Nt$bcu<}^>9}h5yE6)LK(TEGk;#s@yS_XTj4FB)EzfTOEru zOqFfh^W0abmV0D)voR6}vRT57w1~!%%`n*1uOtfu9EcphvmN1@NsN5iv2zmgp7`_3 z+tYC=8xR6RTe94vtO3UWrwxWD6i0*ikj1H=TQ>^E>?E#-WIlJV^$sz`O^f-M0tDE3 zT(OaPhIU@k$+`F23XA6Mq)&|!3;8W8$w}L8eBX6{hh6uCf!uw$Qi~+tscKgnOEs^v z38jS3%;`j9J%3<@Eha7|y#h!X&yc7h)!cv#f!^6YoWJJ@{?+46g~=9xbtu6Y{LIC< z02x1UswUX5Cpu!z1gia1?)lYdTeY84*>G-lU#e(_O)L#4gV+K z`S~0*%=0Q?QEX%;SEx@U`3mApB_<7k?%Y620@PRn0IssS)>V(cjn}P&xEySkXN~$& zAYgFm1?-*hOYx3dUC9u_^>SEHBkAxCuattBK4abG=q~%OpjxCb&S1ZK`1Kgv_SI!C zKnLXJcd+B11q;9OeZu&pl!JiRu2yx9Nfb(nR&=z*IHUF8{N)=4o0D&pPI+MOLQLkj zi_>h1>6QJBB?D0Uvj(%X2wwJB)o%a%%d z@yc?uX)a`E%KgK5<+D!@3WJ}!BtzmzN2}3xorTYHHH`ZY^reYxo1blnzY& z-?|%@8T%f!OIW3%ONk^C7Su|Y^KM7SgG-Y0cpfmfyYQaP4PI{Ve&XzT_~wm ze%P6l7I0ppbe zgXg}+8<`ECpi}Wz=65dDBsBR?pR0TIsg_l=%q^;Gd@>@d`3xX1dIFCSkfdAohRyqU z4IQXeskMS-=OKlsX5crITHVL-N@%yF1PxVZ4!gKz85^itfd+m{xElC~2uY!-1MbM0 zRB}&25-<;@0a)p+X367(LOx)VnQ8EcW0~mKv#I)8qdqY?2ez&}mie@l zlX;JONK21%js2tohxYFw-A^BNI|77bM_kv`n9tr}aFhwU>Nt2bBJI`DY<%I=nocBi z<@t52JyeRcx%NsqCi8lPn8*ov7bow5Qcn9b@nK3coF|+^nx@x%9jbqN*x&29j=DDX z8coywhp(VL3f(C%Cr=FA$;Ra1;O|HJD5*7upC9C2Gp`Ef9Y-NZ-wrL8+ z@23`~q=}zHjXDC>h6NrF_Q0vY1~n4umR11oxpFdnqAT*Pv$0T)eF8n2g5x9wibo)JEGq$+?K zfHrqfd)t{%ZBr*iG;o&f!odjfF6`|SKnS3Z1AiEgcJONzRXu9&4&1r0<4wO}p3EK8 z`cgHj-AnL_8I(+x{P()$|Cf*Z!{QIX&$*PO0D6czY*FXdo)LMCq5_NtoubSdmbRF8 z)SEHinzds1o6*>1pP%#<+0zl3SE0<$G4p!H9aTw^r0!-_?AKdD>u_Pw#OmW0)qTpC zO)sFAMxFaM|9^Hg9)s1-Z?3_u|K`E)Rgz~)yDN>%?k38xtBoC4Nh@$_H7Kw>Vk&h9 z@X@C~^#aHNebwHeCns&ELy6l!+o=r0z-z@mo^@HeaDnRZbx)w})MKW_hhf#1JwWM3 zi{0_k9!vq_iRPYGI@J)`n3T2%3jus`Y2|)n5#ZnrEQ@k~H=)1hds-HXQzgZxu z+0^e+c-vBQkY}?)V=o9u+`K8WHT6%X5ELHK)n18C_MVbiRaQoUoKSjJCuZP-FLMc? z6{Hltf8BJ`_DY8vL=XpKBQ+Cj@aW4%J)}UfAuUSH#&q{%$Od4Xn2}q$rStj-zB(N2 z{!6tsa0ml7Mx?3@g0IdI;G?hK>(vG&nuy%z17Yk+avnJF7F!4y8@2(0xvEbC7pzZl z^J;4h&F}K?7Zyby_uVLh$m^~}H6ZWJcCUDk_VJp%E{{zp?EDI9x~pS~)Em_`6@4+! zfBp{Hp=Gj@E5LT}{x1GeAHJi4)1_{>Vxn@OZeN8*$JM&Z$2O!@n_VYqC^c^#3kcj{ z*0A#0h`Q*eWFm&*i!Ep)r-)OA%j3Qvg|;2$k~ZLw>|fx#1W5);L{bOq)l*kn&q>~S zS8LgCUajcZ$BJrHijM+^rieCA zx4OA=+Oj+kHW=UHV`VNKMT0*9MnwH@M#O_TWTDV6TU#ex+MCHWsO64W4-?>fg!)L^ zaF5oTBSx;qs$mF4!c9sINddaUO%(V+%$MTqUTXO-;ydB27c@Gds9aS!@E~;**TUi>)IH*j)@@^A~6Q9%8 zl@Yo+bbEFY#ngdlQ9i~Gz^Y_8k?-ZQVgntpE^vlBPyQVn#V`Gj+#mc8px+#IHkAZAmx7Ll0Ac=UDD-1(?H8G! zgfIE||0^FGua-4PbNG((2VFY68jR9=6N=%d$5w&mhn8I~PXLm@88tuDsKQAcgD7x0 zHgPd_G#vn~FcziufZ?SN@FLbBLG+$@9c?2LA>om>ULG1~-%DrF`QO}^jV-2?1)$rK z;La+cL{FA2$XA6Ke?JpUQ9faMM7)gJ49C~2ADqmd?W)!TLM3OEd$>1uKF$FzR3kF@ zy<+0Y>3RK7x~LOMkMo|V-3tnpno)Sg)vNTp{@r4Fu~TZk9zD(*h_aSHuAz`Xp!W)sMnD; z%W~irq@`8WR@DcoIY?=@dr94-1-ITV|1rby@0d6LKb=F56K+ayPSHHqCJGaMJJg98 z%#(gxBpI^0oSR4)6PL+VmdR%=wCJEUzS(grnS1$$^Va3-Tr{T-K@XJ%%EASm1{z;% zi}~|-tJsv@+u5Y_U3CQurmRZmH}7wenp%r(IJi>}qWNUj{f42<^*3%(-<}%DIj+wa z%?u9l8&JzWqTSO}aAKO(%R;mDqbH7&w5}hSnQ{p#Z8I6}(1OD}Gwf0vuC_L_ofhX1$+D32y>u0v4PA`LZ+cfzXR0!H2Y;383s5d%kczmL4gl2^DlCy~CO^ zQ>l&X>l7)=s6z!*rqPUDBtv)F--h=5*r0-tsy;b#;G*SAe+iJ2^^LmxT<(4ZsHapDdAu?1S zzJr3L%``7!v$*OQVl=o(S9{5b)A4xeYGM43dCz|aWB*m&=a#(FZ(q@kU)~TTAtv9K zyN_Q|NY5!8U&bZ)ivE>x-0o5c`oiP6?lf4O0|R#Rp}0)XGBoMYg4yhTaV|g4##Z6o zs+! z{&UwWGTm@SsVJw?JI~cq4o&fDRePEc2QTuXT+dxy>$k;5NwD_QOQZ_sBb1e}i!Wn)T*y z_y)t2x9Z@#dxIyzlcb>rTX`V%_zcv*6sb>hV9Hl9n{wWOY(&jEc2i*Ax)-W?0*NTX z;81{VbEm0_x$BUt$V+i~KT zOE2o(`lv|2QYaAux_hO=k-UwZ$0?^dFBBy63dNBs1}vrDeuMfGACud}W<(;?*LXjv zVF2pp5!11t;4EfJgl2T+j1#urxGc?D8ac~+Y|rc)GYTXNG|06$hjUp8TT2U^AA4#|;}+EkHEEiI=8 zWGij!HHW}?PuQ=sAH*pSd*#jVKO~M}v02LVBzOmgjbmV)- zktdS2+`(W<;m7t+djQ!C+w#e-Ha%W(Cmb8}wwVhv;{WWrA#wjf5FaH__Hq+gdsAso z9tr+Mm(>4nCg8Wa-V#@S0uM+1Da_lqHiyJh#v7yVnEg;}MFkMM;?Ldpf8cI?=mj9z z`=mpxn!tJhdXluD%|bwzUy>Nm^J}ZHLXvA1Wwp9ekzaj+y>FNX)q1wg1nhDA~7kPs}TDlb2XhlAi>HFvQkg@74%6@M64NhwmiU2k`D0V zFBuZ$USwIu=~$6vOgeEvso)oY{2e)z$uG#d*hx_?M;ByZ1}ifQ@HK zfwe`*l^L6xy{c@|j2d6T*L`-+2}NJ^BRMOO8U7PJ5B-?VBa0e(Xe93pjys)u>26r( z`w%`!=`QQEkkbHI^Sv_XHyi`1Gs7{3dy3q3)(=>)3;luM$P-y8(QR<^7O9b~o>$t6 z+kspEN>q;BRbt|3+?R9b&veo)q~wV7dtdd;L^{u{utzS$6j-t)RZ(;l+Ic*E=HYnT z@;!A_G_d^WyOAE5z6>4@$zTUAK-CokCC)(qPs&68T|xDKeje?{VIG&yH>zH1$|V=g zYB}(B(AG$@8fCDOWC8o09qMdD1hhU z;oowz^OuK8!$ z!GHa=&?XB3S}Ye@!3XE6fmP$6Yw?E|qNX(zmiv0(M7!PTvS7cy$Nq3szXH`7nCZI{_xm`p z&W$px-cs9i`1r@7^It&<@pW@g@SpEJ!~>Q1eFe!+s7&RTkL?BL<~=p<5SB9LgEphqV{0Fh3m2RFN7UkuVv9vCyOvk(c zkK~i9bg`y*sd7hlHNfsu&Y!W)2X+H3{a-qoxA1P_8*nYYw&Y9C`CvovDOz0NfW2u} z&^9eGDU#~)TN?IKX|*}Uz%Pax1*-L3$2lt4WrzS;W2aa>&&wo*`gNA!n5+N;2c^zO zl!WlGI7iBxc_)4f6CLm^pdeoR)&E8G`Oom1zWB10mg}gMR1>n~%Tb~cAajwWA%PTjs@Cqz*=&Q<<#(yzm;Z+5#OL#cf}T+XOnPQ{A^Opu z2@yS3aK)OOvJ4?2<+F2UwDxY@Oi-@o%1gp}_3pEeP}N-`8gJKYlN&Nu$hI0A0?5K~ z#n_i6;^!)$FLe9y)cN`|r8D2Go8~ZTteB|qekxPG8#$I#!_yiCrO(0G0ug19pT-n4tg{IR3G8GIG zwE#PkI{9DSPwv4+N^29>_3`rjo%d4JWtiQ=h}kO;`dvF~6ZjlUn407j9nHz)XN@lr z(=X$h{OruCdFj2^A;A%>Sl5{+nLRRAeMi$5+aGf=eU$9p5y?plIe-QH?&j-H3@!iO zzg>JLWLv?a;HtQ_az!wk@!}LSr z3b~>FUg&B;B5S4ej~Y8v84rg|`mb8Wn_q0W4w(z^$D|iwDsO)hHG@b`VrDw;j#jx8 zfX{lkdW)rHrb~253V7bteDVp3`b^beGEM)wH0!nYobwH#y z{r&Mb9|wGjZI}7E2bI#g7mq$ZtJ9&qC4I&TLgR_F%;$Hrrt}r+FmCvL@yX}26pFJF zk#}l0bE#jaO-<7nwNVH@^GbXOk+`YCUHTUPDo0VaKXOTv{&mP!ruGg`vKEHY@@TQ9 zHr8TC_0Ch)9Ti5E1#CHtw=5iT>YVqm;*uzA=t^H0y@3CLymkJ&6}?ebPwKKBpsxmQ ziQ!fS<9R%reaaN$3Onp$-xcQ4etZ!|BL}7&IC?{=XNBJaCWD2Il(cNIwxDngDuF_< z3igh1eg_NiI`>tKlR*KSRo_CglV8BDQbNB;9q9+8q7+8wv-{ao#cMq_6KJkJct-;7 zWJ`bMdkm=1@=4y|RrXeK<@JdpCYF-zq{0Xz%`cUbz7~ysRY>Suf;ZJ5i{%;%4fcL{ zDq=Cr{#xLTayiKYE=Z6oh(D>6AA$qY-pSXx~Tr%je$4${XyD=$Q|7a7fW4{V*4 zc^3nYYxALh+3H6`|8$-6SazLT|4aXBugbQF9^x9y`$8;(N0e`^=e5@ZTLf z2Bo%M-wUfmQ(2X`YfHewb^=p-B7Gi7Cgk73&)uCbn*Lpr)>kjU&KS*1_lR@^RY3TZ zcrJyjKfShbr#7N_g6M^M?4b}QwKke(hvvw+v!jT+kE0-oR=R(f_@1?4tZoU+5D9LH zT|Tv>`x|JzzJd#4pP-s4%5?K=FSr$pKZ#frfgydXYOD^ zHkDh{9TtP7vRGm!#qGU9dbUjiCt7Q7h^jqg7MS|*NIz>mXn|N!Py$}{Z~p4bUI4 ze*d_07O}K}`GFSYQ)0PIroHE(nQr^79#vnEfwN*O*l4#J)O!!07PspAwD{w0(H}AP zMeOY<{Gn3k*t0UZEZ1;H9gH+0e2pDHXt}gkzep+2BEAI_Tz`*$|I|CdZ+(A^lv*`n zw>}h4dW1~++A$YRS4RX(Da|Qiw}RFqR$G7?e8=DVl;4l{=XPFzDKRZPb?RpS+5wM7 z_LcxpXI{h(k6^m+F1p^UA)53U-83)DSn-F}T>SnK?0+}X{mcBH6NCzDdP;@;ZbP{x zpbS}TP&Gfsv*&wsU&STZ-X_}G!3uV>Fc|2Wbfr#e+NhYLI6>b2He&Wc@KUu z6y60}5W;X56gk4lw~)?ofkbF)tuw&NOAi_wnE5ndpK^8T6axyKRyg7qX!;4!`wH2< zCVmq#d#XI=`;_?$O_>5x20%R)gba2{T&ZG!PKXww+P7`D5cg}VdtHv7MjYQC0(q!Z zA%*1Zmn3{@V3;z4u>w+DAa?>nx|SubRHDCvCPWHR-}^)VV6l+@s&wJE{p{Vo2Azka z!Z{%OtN*_HL+B?WpvQ`ZeJ}Og$DVC+&t$q`ZSg6qD=;)@RM_`2KpJjsz*kj(QgOK3 zMJBU(tZ;udeyLSjb#X$q3XznRgqK>{dMKuy5aTar#s6iN&x?5J@)HTuz`~|ESRjc2 z&}XuYj}_<6D2O1k-kJkg^=;y>mjzGvPF=JCy|=&>akJMoZqMS+1%7@ETAoFaSw1uxFH`RfdHYR#4P|WLwTcQy(Fxo3Z6h2*0)X0Fs*dBon7;UTN|kf<4J=?7OKX zm4})ogBH7vTMiqRSoBUF>8JQFn=%|o9kwfXrq%O|tWGgXOf6aNaS5P?Dj~%67cIq- zU95|iws_YnS}4|bqquXbDyqu-%`2aAT+)0J$*co@l1d_stZS19R}7nd+$#S*L8n^H zEPD!X@ly0LKb$W+qS7N!rGN0a)*&tln~*7vmi@apjsD7E;@Ws`O`%nRyWXdgrABh; zRNc+jvs>qu{S9ySt@BQKDZk?c8k~1gBUAq%G5=*z-}l$QW^A03MMfW>rPHo?-Y=nJ zLXG3?QbtyM(RqM*s19;*`3*ao{>R0|+HA4nmNsmWmI`z{OCVp7?o}{(JvgiB_2mXT z;f|+NZ^cM7vRLq*NYs9l7dqB#67Q?0Dw?*6alB8ggymR#HdBW?S8_?tQ?18@6co2HP&vap7^#as-MiMA_cv6JFVq8Iq6PM-Te7*k(7Vv8e z323K?2@8~Ja4(lH|Ft@`|7p7FWF*Dh{+h^mP<+S&Kaj4I z8?kg`DtcXon;}Y8(IT*S0S8>D2Tb!((;>(L(X4~6!pfqp7TX)tT(AX%@PspgeLaP& zLQrZ$`+C2H6ntQ+=pIvR4bc&yBbwW#k#4kJt=P8asz>~K3(;fy2nJtaoPOJ6jy@No zuOOQ>QR+}Ux1NW2GqKJ_+sO2^DRN=Mb5@2jmDD6%$g(rEhlR#M?|muzgY3YhpB3jb z!U)^}GTsFC{i5ffR1gR^l@bTYDmfs(g0#d-5QPD;ff==#2fILB?{y(mKTy8Z5$X!i zb5fYnSCk5LU(39cp^s7?vpwnj<>U8Hrn9*|p6FiNI*&D2)r-0TnnV7dlZDkQGRVjdphAEg`6zB;c{jem+!bZm)tkJ$m> zAS*o)zfzOB0~p&*@cTo7b}#k3DYizS8>dGF6TMBU4V&;*%Ut1r`l*)+i9)7dA$*?! zvXvi(rR?UyE<79(nIJH}N$mU33U-{%zE@z1)TMWrJ6E)dng(RF>KgAw$aE&i7H;r2 z%P{oI0h)r#vDBX$7@3U${hkp)s0645;G>PP@o;*(EU{UfQpZEB&T##>KEX)^lfF%( zjlW(?9g+Ta*_Eg=X&bw4MvN6XA*@zHUz}zeQpS#LkGy@iAE@?}7j!VQE0F8ZNR3pb z^KY9@L?!h;8eG+GXb~F{=vXn1p~NxiZH8$)m%nFord&Qvo>l?vum6_`=>98H`p168 z9gM7nz(>}s`Yj-@a_lOL_~D;n%~S5^`?&5=|0QC31l4u zDrX%BVgT7lp-RmCd7Y7k_3^OXYOsP99htyCdev zU|vMK-<jCZlS;Bi=;_HG8S-_hI|5_E+ztO+d@czLJ^qs)N_=p^)5SeKzg$~2pbTfa2w znKo=5CDthJ)esq+n7C#`5HLE%G_((XQo@Ss%W(?Zn1Kd&`sDfNg^x{$Qbzi}kIDPMu$|&I^3>+DyRx zIM~6F0$zRS>9QQh56;m?dNRHoD>QeD0<-NTCfr0t`O5&%M>drN9s-9{`ZC*kP&3gC zmS;#E)n;zJQ3fhJFTV86dgWo*HvU?oDeT@e(KWmn+n4hzEDvl!>sj&K9-(Ebk%|q* zpEIDg>z^Vl0ma#HcqSe0|>xh{nQVa*wdzYw1M`z`{xj}k9AaI6dJfac?6l=-Et zTUEhvthpXC575MMo=6c~9M(<;2nkh5E*HOZjkYO2^HP-EiXl<;RsnNu(Bjow5-Ao!2IeJP z6?<2Ac}n$}4C^M#0A5bbFTWi!2B3_On#f;OwP=t%xq3_-xL1@{lM?hKC6I{)WQAoL zgf8Pf5iJiOP5TVc%Q@144?FW?Qc_<|GY$d*YXg@^(I8#_f6@8TcXtFYcOFW#TtkD} ze|}S<{W8Gzd-(B7lPYND#3#HXdf0+xl>v{6cw@RO$_3=0vyUu~v=Y98f@IY7nei{X&PzbDo_8FiK9&4JMLv5MO zu}{G2Ej&u|+6LZyh_L$R$6@@(a!d8&p~a6nu*a;vuRv8$N+}jcv9Z}<#kbD=W`l6M z0LzVcWT+wB_GIRvo5H5Jw)OySVVL_d)^e@Oh9t*|_BgZm#SwsCremq<#U|ILFO9m|V~}_2t~5hu$>sr+0uZi* zWFl#rYx&LvJT<2XzaDV@gy%t2W6c;}^*gcGso#vtKF-U-v$UQ2n!w zScwSLCsPlA=08^Q)#_R9h`Hx9Ft~V;#a9q~?`(~I`I04OC6Rww(2f(%spRIS*yr{4J8RIR0=?BV_EFCwy@lGdUVv?j`rDE!+b5FT^m+Nv(3{Rj(6EcFiP6z23KmnnZIqP&suL;a_lz|2 zaYXZm-bUjEQTwsSVjM!4mXBXSQodAk6;s~)!(kD@x?>4sQ*T27MwjznGlN-eTr_avgq-fQD=)21r>?7ub`XT zq6wKwzzy8%<`;=TlW2E*ldx`y@JH!Plhov`d5x{=9$?V*$Cn&v4~qQM5GjG>a@jZf zd3cSzR2T@sSX4XI;PZEY^EU-OIf*z{W&h?}SVQcFn1dqb_=JvJGZtdMD3T|X>+~3K z`!^wT(+n02Sr4y6$uAG1C#bq-&_+Y|cz6mnW|@lGKSbFW_AWCWXYFqu?q=btU(u)gEcU?UIY66~P)>=5&Nm; z#5=~~SXxW=eQ|K%qr^~FBEh`}p{^GcdG`Fa;_5;{5g zXygd5b?%>&nBHW0vlwRV`}}^P-dRMI4nXv8(3QY%eJs26Zl`I;muhNsU?4?`GNOC? zwlU#A7U~K>B_W^qTOrBZ6z$L*77st{2f%vq=07N@H^2cZ9|$_JFd)G1caS}YutEn- z1AfXohbHU`gNY(i}@eK;xC!%wll-IZ7OBPX98_4*VZWBYPX* zQ|`BT9&Ia#SNJ#0l0~c;7`V4=owZSK9F;BIg)C)s{SaP&dL2W2!AT)D=U3y0{E#kd zbgTeBaS)JXfzDmr9?5>1A^W~B zA^XmZb&Q$u{pc*ePLFe*&Uw!F+w=Op=8sn-tD^;52!5 zAGv7`Pm3xkb}?hgJ{lzHX7QNm@v=bpJ(m}1noE`M>$=#hkT?rfUtZJO5;L8}9NMc3 z1U&&H&Ora&as3SDG-N?yCxSD$`5g^!U94C}K_|id95Y=k4wIVgHt2R!lXFHqu-1JE zE|`Y;cwJXBB-qt9R=kBP%SlbEH#SC`F*W@nH;p3a@!@7oS;f?gHDnkti2LCqdXwHL zq8GYiO_7*JvM?B2<%pmro&nvWHnuK9#_w(288XWx<~mNoe!4SgLgkWaU}-ox0f&At z1?Rtdt;)=S5qDUT)mM9#PS#B*$*3Y_;#o(?;a5%R!DV_m5v_%XU7-8Q9PcSrr%f7w z=QT;){k;eN?B~3^Wna&D<%Ub{2>?S-`tvgN>pNOuK1;I3Pb^PRlfj%))Yp@I*p*VR zC34+22N$Gqav7!;Y z9E>b|ir1-E61r~^ma3G|%JD%We-QSFE)!i;s2*i;@rKF+KB7E9k4ZhG=KVR&FDprU z^_Np7RjW`*U$MB-rogj{K+C-mfB085C%YTAx@!`uL%xhi(MMM5X%H&F7|%9BcmC&j zcu0*WnRN;I9Q3#FYyZ6jKi6@f7L9ta=*w$x-{EYN)fp%ay3ZGtw zFn(sp>&yx(={HIz3?oE4{$klnIRVRF(ZDWsc2QH-Wo*pj57&9f*g9C=-W54H$aRd( zNRK7~Ct71fRL9yyCo#3(C`7##yC<8m9MtK>; zkps(~u4=8q_KU)nBAl81_Du|zuV|q=?3qE!>=K+>?yHGNCc>nCcxH-)8DxKTTHSoC zXR1_5Hgdq|3!HYUm>&dV5R2Bed^fDpf^=nw#|62+7KjD4% zMsKw!Ddxa?uEF7s$6R~5O^&Zv4Z@m>l*_wUmg#*>o{4Q8h4+@gx+T{kbm{|e$;y#0 zp^7V#Q?<3cS6xKhk_Xo!_NEnWQ)4#`5r@g5!3y0pIQlf@AJ@TC>#lj zkF<9}&p0EUemU^;JGMV>2zWp zt&w0evg6tlpQbvN@7{LrwV5vO$K}wA28)Ss@5t??)b0GDmX;MVDgBk;meRWk8at8_ zhk%!Awk>n)(J7{i9cM@OV@UHaV2QVZaae8uXqNj0Xs5c9=H7GV&L6dMo8MP4V95Sr zpom_UYE4)nJBI`R(nAB4N_8@qi^^}!s?1FRl`|oJG7w6Vx|Umuau3)JzW2Z$+{u@) zLEE9sOL4=WlVsRlviG>3lHG`=v07sDON3N73tCWG@7PRQOeu1>xxemFr zLfoM~pnZ<(b5*1k7>lJ5G(F${h_DV>E-uDiTFrY~s~F^sd9)5uFL{^d8e~{iB{f!b zy7+#kyw*Jx(iv*sAoc*WikCnmhO~aK5jEa|(Mfr6>yW#kVd^1_4j*z@0Qt8w16_PX zJTXWGitSG)0tYig?e)L<=70T+>J8p99ck*Aw6iOlsZ#Jvk~%K|hwms{MnB6madGi_ zCY;;k$-Q`FSg)BN_A)Xn#KHv%W>t4C4kfyrvs3f$6aXi&a~T-ye2868!IW?^q^oiE}(- zSlqjE`<###>`pyC>*c$S{`AC-)>}seCXsYVqRPVd9R@f(exUwJjZ5= z&0SeUs*LM+aJjp9Pp?vi>f^fNqWs+8wU+7>*lCOB@9_x(XA|%6Sq-;lQ7b%{a)^l< z*Rdp-D+~A__P&X#7Z=gfUU{|@IS@=*R>+F&Zttt#wTWS!>%_6EoA~( zKjyEgv)@Y<*WWUiKYD{(>JtvC4Vw^ef1Duaw6(k`>lT6GDAhdE7cjH~M&!L?b%ET6 zJBdj*ede!RO3O*!!ootFGEl`ry+s081?W3Df?O-SK-OfXgZQtfUV&91pYk%P<)R>QhP8cN)Svua4Vvp3hd#z?Ig?ddMH2y@= ztWL`H4Ud7n?DWFhLck8BDcrhLLul+jd*6 z(!$+1fRPN8wSesE;pZ?90;U!?TxC-~6SiaAzmp>ceFv|Z?v^ZzQop&NU#x+A{RM2T zj>me&*fCvl9qw{LcA4Hm4-C8B5;W89`sDq~My4S##zn>Zb$iWSdKu|tnHH(XjK7)2 znyq%3yb6HgnrLcLoX?QqM69#j@2Z0>eg9Y`bmdAkvWD+;y<+f~J0=ri|8`?S{Vw$Y zQGOBBwkj)Dx2`3Fx4zIT9kyEQkXx?Ot#7Ofm6IkEkR!2Cw@0Iq81AoY9-vCscoK0M z+aCDNTznRFZ>n7^28e?GBV&sh>Udr8+nvy!$h`ba2W}_V5(yC6-I}-9umf))_o-!( zPl}q2?K-&mO4C6=yrL&FBVGVD;1NlVYwaJXb6(r9yY()6D3j2*-CAegVC!GJN+vG0 z&J{KzzY5Vwd3_tU4aGio#ArfsVz|QB9B$(e(l7a1y$;D2m4bN}A9UK*9p+F__;swV z6@C0U`RZ0ZLIB`H?a8G?L}Q)=E3|S>si~f5yHd64iKo#P`xcDhB1wo=|HVNLaOK}}SLzpL;8sdUJxS@)e3e(LP=r3jm z4B-H6q;d3|C2RoW`L;@;Qe^kJ6LTl3U-U@5zprMI6vQ26qo$Gc@?W&kKg!tcEN9#A6%EPgr3Jv}4}s)E zma<;Dc@O&1ePb!=O@gP-=PUNshVsxa2XVjZD|;_@K6~*pXWQL2|5HBlyWLsJ9l@sjSSDl?#Pd!Ms zy9L>?{lKe&Yv*iE_a@@Qq*{>NW%d>yX<9?`vmHjgceIk;6@R4IFJoO8n)m3(0nTO#B&r)6FdAnr)YW$>4b_ zv=jW05%2enL@%5A&p3!3LkYr$s>umUXaqHf=zLvaAmjMBQo$3QpqD}NQ*fm_;-Qn> zY%%j8Yn`Qs?IkP5B9~83Mm+YwxhndM00TeECmg(wkcHpK+&>&h`^R@nx&I$nkH6`^ zB9iC8L}H-(-vYWHZ;_dVLj$gXDa~5xqC00B`0&B#b%@2(+&1+n7nQQufAB|-qip+L zz|ZI=q@Fp6i1IA0pqCRFY6N(yo!m^Ua8;D&!U~l@q0?2gh&`dOS?QC;h12TouMTBs znO6Fn6*bYp^e0l)v~rZ=#x%;blU}`A`arB{y}U&#k=NLg!^^pHEeB5{RpoM&w1m4% zo>riqYES4g7`)Rnr3T-|N`#%p9hFmrI7epkCz{gty^|r&)e+N#UUfe$&^%r@#qm@( z;UNpMt;~-+=^>lSKP}+*tV*CPl@#B+W!dH2aKlBpMkIss2kkKHCl@5^2VwlcW^++M z`y5?zsu_w}y`#y@P(>~#m@EfugC>eg^4E*?4*OW?oyb{jtL7GOnbj)MGDs&(itghu zOi4>q#rXnV+b#u z(V2+l$vxHSe{&etRDgI#oJ!xN9hxQ)J+@~C8>(!Vy11RueV9raegW~|N3Vf${=YL} z<3Cwfra>f=)$DEDk+)92hiHjkf`yIL2gLsPYV8n)R>V)TRj&AOhRsEssjCbPG8=rx|2^Vn9y!WUHc1>uyb zv;g-iNO@K=rRuO6ZzU!~uCc3V^jz&o)N9s2Ns4kT9JZr3eiSaX;gq%xZzc*T*Zu_7 z>3lNdGL*P1Oy3IZn9HR(;E7O%WV`-2aSK-xdw2`AOo8Ig%|JgNh6l4<9 zh2VK>e^;K@Nua@GVjU8Equ7J`$f4C6CvQJtI?i6^yiBr~fbE&Qc?C-f#~yhb64*Ii z>Phf4DmK)@S%YE|>o2(+SM3_xK-#)R>e@fsN|fuHiRhbh#?W59PT-nRSuB} z&>=%RW5@C>wgYTxFoJ^#(Fd7mdf8|X8;jz+o>yVh!qYt&dX0mjF()0OS4N}YA`Pui z3jmCM%a{VGWg7Sb>pEaKlOnU)1Xcq8aCnL=IoGp@nq)|UtLEo-S?g}fo(WHOIjhu9Q{#ipcu z5NUXJdpn%E1scIvU6gUrGS?zSh@57sUKOZ`ejfQY5~h>DZ*GH-mSxn5-0bZ#1`~ZM zNG^X7lF-#X!G9N$(}cs0hGUZej5q#0FiM@|J@VZEwXpUuR+mSuc z!~10v*iL0R*sF9JHZp)B6Tmh7Tpi95oNnsLN@+NzalkvY25p%+f4!k*7$XBl7Q^?7Wb6Mr9&Fk?xk zb2BNzg(3QYsT5h%t8J6h2DoflaQuxA#W z2NLheC7kD6DdHuKPp>FI%GoX?9VCtB!>Frd{tIrZy z^xWUf*u!P~akksdBc#TB#$1IBpNDD6I*m1LX5BFYj?R-d4C%gkk&emG)x4EJ@4rJ( zXNk1;Kazxu*NSTk8-qn=K1tUr?ALpUlKfZO+Eye7D$7#^-vDNR6JLn_VNo=7w8z?LmWyX^po8bMw8 zQF(5hYoN$7%&1uy$P!*03cS(Kdkqne$VIH7l8}O)X^5 zfH%}S_PQdh_a6PoCv<%4i1ZTEe4#1bxe9ZFz^88XGRAv4>uL^{YN&1f2%w>!B8yJl zZWFPVI`3!Q9%dmRv%%ugSk?H&Lf|(W#%{3v}6zEmsV!2)2?_bcsj>hfPVJpTml+zFQHy5Vm zOK_wCK8Ryb{dC^GTe~^eGCz^8cgQhsIMQcCOV?aaC3M)16Sx^?HRNrS;g=A1H`_&& zYri%E&NKjHJUZ`ceEufsfemI{ei3btK8*ihH8Q0e!7`|PAGK0rO6XueXXH0?i(#bv zM6Yn&c)UMWz478EmL1Ij`22y_>ySJYVGleFoG`bnLuQi6o_bT}C|Wm|_EUPIQYIdC&{a=EjXUY1ORB^s9$Yw*_`Hde?tn6%{L4gZg)CUB6;Q1O61La z!pD;VrtbzYapn%}$>meF**-^yG8+F1_IY+*3 zR&bj7f~r{b5d9LQ#bPveY*Nz!#CNo~evH(|0hZ%YLm0Um3CcQBjF6S;>IBRN>kKGu z-;+>$_#d$%zlkhoUl0U8(cAZYrY!Aml29NsnAY4^zGHgM*4tkiLux5SfHo=tqH0Qh zvG!)ZgmPw$NjFYeQ5FgFkHh;S?cDaQf)LX$313h`NdIKrbq+kqShl@X$}_GLoRT^L z4YNw*d_6cl>zX6ZGQ#6xvw9e*6!bP1FIAV-FVlFU%%8d`7~gK`THE%jiIBL&HTjMcWnk7TWSvk$5r`~PpHE;{PHU?#y^{|{_*$J zWPG+Q&Z)*f-|)20c!Th zFR;M8ooC#b4<$>AG}H-80C!uIF>YC?2$~7Z4-h?G zu>)w}m1fUtz4oA#Gfv2{K(}IlS@7y@WTFkz!gFu)H!zhN&IYd};uGL!d-VM%cfW_A zW-&-Ce*hg%RMqD}>|5Q3^9221>wg^c`L2_-1>Q z9J(9&yJn~TYtCe60`*7n)HVrjbHf}FNGWHVBQ%M(ttQQiY8V8BpvtT=@33?6n5PPa5Rmb=KxtAK=4W#z(InfmeXfX{^ z0Y4g#2`R{|Hvo@=0FLr=pftFnq;0Z0`4dB^FD}g$bL6LbPW*V&@6Wk)20SFsleoE( zPG)7eQDN6~#8KXSo_QwuwWpCEoe|Ho(h400#4?A?^AFkP*H}k2BHtDvWe9rNdW{2V zw#_QU4w2F|TAhRzWFffkxCE9G`TNtPq^LN{+SeiG*!l#z)B3$12{WP#x7?|R8lla_ z2?YVF7N<-TxT)vS5wjdZDwWO;rHOQ~M5n8v_^V5WLa;&kMt=}n<|2Ik;f-z$N>~A1 z8>R-#m$X_DOUWcxfu;0ZQ0nT-5E=|n3Czu(t7ZLHmJF4$bgO| z*fEQVJGZVHEAD-mv1OYB`WfTQbr%OT0`HK9G;`GIWy|5q7K9D7EPi%=VW)bHJ`}jy zA;8^k2QqsKdhB~4Oga9wuD2+8f}mGtbj-a6@2L@-)bj!Y#U4Xuy74>fH6UP~mAGf| zFRveD!iZ86E$VCaW?Q6!eUk59O6WcO8B{a~t!(*GZ}Z7#0vK`v7%FS7Ln71(c=ahg z3nx&n!pR05Y8VDD{NH4jZ5A?gK2zeRH%+MRcFEg6Dp?Kdfs?aIy!96HGOeu2?R-`` z3$*@YUmTzze^K1^himAU0z~45-9%;Ox1~z#t6|+CukkVHYiszTh4zA2#x(d^TPe2W zpTw5N_WM6sMevzY`5lpmc^oUMF7zEF71zm?*sMeNjy`~SP=5=NKbDO4aeddBwIWfy zIVn6bWf7DKqrJ$v_NnHEe8{N*Sf*j-81ZcM17tAln-A3ahXqiY{cEYPFl`1s$%TRP zbW*~A!ZDLxzEr{d#s?%JLpR5Yl0d!b?k{Fss82NvK#vo1)`NTE||!SS_~n zU1mv@Pw1Ef@GGDf`Q9R+9RE57E^k9I?}U|Iyfnfc_2wKsdCFRS%zyDvdPh9I-53hIr|^pKHmifDEInfwt!>9XE&A} z3!zD_cR>?^Wk@$SM8c|Gj9w%8Y!fEWRmgTfzv&~VZ0A`cq-F@dqe1P%m7!!!BX;qK z6Utf1eZwqhOP^tvGD}9yYn+AOzYNW`v;I^Q;+H2A(vYdX*Yh}Qvpuqm!-BG@gKKI8SsOSfzU0F0}voj4zllCiqD{&#WSmrz{i8%%usa2W^ zgSCQ_#q2u$Hz&T}WM#~3*xFucm-QrsP;oD9(`nn#>nAg&OZ>T#fNS=r@dN&qq5c2+ zXIplDdF9;EAa$k*lU=B6w>9gS$-<2xyg~lg`@G}M3l09=Tm{WXTbmFrx;>zyvGF?+ z9d9tJa%{{Rd%3*-GRv#j=0=<|x5B3*ON`-PQE}eiS%!>uaQsrh%KlVnj8V#{H|K>n zwC>VVUv$C~Laj9gy~Wb1-PksuS_c5}a_b-c7p~S$-5lz_&84DLRc}qEWa3`k3qF=I zr^zB6vOLt=-fj>{vt;$g(L3WsHrp2$?t z6fOVjwEXvL&y$sSn>{Z1n!b|o;ifRFvdd}QqISlg-+f3JnUSkk-o2V>!V-EwD8JQ) z%%oeiVt{vgHRT+ZKVN0b*zAF+w%+ow+*&$=8?pB)YUxmP6`5$>KVT%PvR%!z+N~J+ z$hv-)PJwA~lTjS((^^Qa_$xKHL;Eq6?GHfnm!;3^5WN#oBsSj#rpKS!4e2_^u5i2M zxW|9kg*b!{JvI1IarP;C>9Z-wUGFsrJ>H!4jyyU2GO zh0=VhAt&CXu#sdE{4dDLt)mgn1VrNYFG$2eX6ZRk)JP=ZM)m0V8m@acESsu5S_uz@ z;r!g)9|?z6)P3~PJw(C0`mWHX0M{knHuYf}yk2sIPL8#do}YDxmj!<$TP|=l-{U_0 z;A;NpdQ#5+K=J#3y0(fQ>`Ltv83B8XVMiw2*z* z5S7?1=4K4XrZG6~E)^cL58uBId8f1v;V?YG#2?K*-hS4<34g1jcIYJj=0F#$?Xv`I zR;9l~GzRdBxa)5gf6yj3>5{|8!*nDhIhR$3UfdYUm2(1AVd^(9yL9>lgWz!saln|1 zN2Yyv`;;^@MHZb7)3w?zyC{A$X6WWJXxm>h0!9_Fkf#Ne$-|xG8#!ZGdcPwhZ{LHq zVaDN@5Of^3An%!oW>b~3kD2pxr0zzQiq{_OPwWeD{@6(p*14Vi&iB}rn4Qz(rK|n~ zU-Q_K$Qyw^G~7_7x{;>w`bRdRmvPdm34@- zAgI=>``SR)5JON!!+HY*<}>>A3BpMd>h!r(W2ZAPP6`BnrIiT;E8awUzEi}Ms<^#tHg5Y#-=OGt$OfI}yC!ICdhTj0b;;x zH^$WmB9W&98JGQ@d<}4OBtP(Gp{p2|P8l(&wsDhdQk(BT*x4U07NRHpon!%|@xO8# zl$e5LVc`N1oGYyJNe5)^B(G{AZ7@9Qpy|!K{%~gFTAwE6Y+o#@IxxAh$%Oy*7o|A_ z^JIBL8S<8WnS)u*LGp=V#0#d9yH-}Umy5(l4aEtp?<(nOA0H!@rkq=c9BTK95%-s8 zmkP6Canha*PEN_-gMKa$%i%G&+V?7A`xz(4a~00L=!Af<@TS#_iR!44&>WzXUr;2; zFOVevsRa7r@3vldrcw1rdLeWsxpqHMzHNE-*cA?IXCDmCsNm&{^Q%*kf$40u>o%h1>zcrf*u) zu_!r~x?UV0(>{Qt5p+hRrAd7@})SKikhNVLJ1x(TN-Ct330;63pyX`$xN{p#5xKgFs(wP|3&-TK}W0DKur0WoC_C>Wq% vfPw)E1}GSyV1R-F3I-?`pkRQ40SX2v7@%N)f&mH!C>Wq%;16K{y599a1Hnym literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/lwin1.jpg b/Graphics2/Models/Plane/lwin1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c496f9058d920271581c8b9ac8665fddc1cb9a4c GIT binary patch literal 23770 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 AkpKVy literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/nose.jpg b/Graphics2/Models/Plane/nose.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc9eda207cc9dfada2b4b4a38d9065ac68ffd773 GIT binary patch literal 22896 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 literal 0 HcmV?d00001 diff --git a/Graphics2/Models/Plane/panel.jpg b/Graphics2/Models/Plane/panel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..79edb90fc42fcd7656b5346a4739a1696dc98d28 GIT binary patch literal 155905 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